
Pythonでやってみた8: ExcelでPython-AIモデルを使ってみた Excel×Joblib×FastAPI
概要
普段の業務でExcelを多用するため、Pythonで作成したAIモデルをExcelで使用できる方法を検討しました。「いつものExcel体裁を崩さす無駄作業がなくAIモデルを使いたい」という前提を元にするとFast APIが最適と思います。
今回は下記流れでシステムを作成しました。なお各種ライブラリの説明は個別記事をご参照ください。
【開発の流れ】
1.訓練済みAIモデル作成:joblib(×sklearn)
->Pythonで学習モデルを作成する。(今回はIrisの品種予測)
->AIモデルをPickle化して別の場所(Fast APIのディレクトリ内)で使う
2.API開発:Fast API
->POSTメソッドでIrisのパラメータを取得できるようにする
->AIモデル.pklにPostされたJsonデータを入れて品種のラベルを出力する
3.Excelでの関数作成
->ExcelからAPIを叩いてJSONデータを解析できるようにする
->取得したデータを指定のセルに出力
4.統合
->1~3を統合します。
1.訓練済みAIモデル作成:Joblib
|Iris⦅アヤメ⦆の判別モデルを作成して学習データを転用できるようにPickleファイル化します。細かい内容は下記記事をご参照ください。
コードをまとめて記載すると下記の通りです。
[In]
import pandas as pd
import numpy as np
from sklearn import datasets
from sklearn.svm import SVC
import joblib
iris = datasets.load_iris() #Irisデータセット
datas, labels = iris.data, iris.target #データとラベルを取得
df_datas = pd.DataFrame(datas, columns=iris.feature_names) #データフレーム
svm = SVC() #svmモデル作成
svm.fit(datas, labels) #モデルの学習
joblib.dump(svm, 'svm_noteKIYO.pkl') #モデルを保存
[Out]
作業ディレクトリに"svm_noteKIYO.pkl"ファイルが作成されます
2.API開発:FastAPI
APIを叩いて戻り値を取得できるためのAPI開発にはFast APIを使用します。コードのポイントもまとめて記載します。
【Fast APIの設計思想】
●学習したAIモデルは"joblib.load(pickleファイル)"で読み込み
●設定したAPIが正しいことを確認するためにGETメソッドにタイトル設定
●AIモデルの結果はPOSTメソッドで取得できるようにする
[In]
from fastapi import FastAPI
from pydantic import BaseModel, Field
import joblib
import numpy as np
#AI学習モデルの読み込み
svm = joblib.load('svm_noteKIYO.pkl') #SVMモデルでIris学習
#FastAPIの設定
class Info(BaseModel):
sepal_L: float
sepal_W: float
petal_L: float
petal_W: float
app = FastAPI() #FastAPIインスタンスを生成
#GETメソッド
@app.get("/") #app.getメソッドでルーティングを設定
async def root(): #async: 非同期処理を行う
return 'Iris品種予測モデル'
#POSTメソッド
@app.post("/info") #app.postメソッドでルーティングを設定
async def info_post(info: Info): #async: 非同期処理を行う
data = np.array([[info.sepal_L, info.sepal_W, info.petal_L, info.petal_W]], dtype=np.float32) #配列に変換
return {"label": f'{svm.predict(data)[0]}'} #JSON形式で返すため文字列変換 出力例:{'label': '0'}
# return f'{svm.predict(data)[0]}' #こちらもOK 出力例:'0'
3.Excelでの関数作成
ExcelでFast API(HTTPクライアント)を使用できるように関数を追加します。完成品の体裁も合わせて表示しました。
【Excelに追加する関数・プロシージャ】
●HTTPクライアント用(GET/POST):GET/POSTメソッド関数を作成
●JSON解析:Fast APIの戻り値を解析
●GET/POST処理:Fast APIにHTTP通信(+ データ送付)をする


3-1.HTTPクライアント(GET/POST)
各関数は下記の通りです。注意点としてPOSTメソッドはJSONデータを送付するためヘッダー形式は”application/json; charset=UTF-8”にしました。
[In]※GETメソッド
' HTTPにGETメソッドを送信して結果を得る
Function GetHttp(url) As String
Dim httpObj As Object, s As String
' URLのページを開く
Set httpObj = CreateObject("MSXML2.XMLHTTP")
httpObj.Open "GET", url
httpObj.setRequestHeader "Content-Type", "text/plain"
httpObj.send
' 終了まで待機
Do While httpObj.readyState <> 4
DoEvents
Loop
' HTTPのステータスコードが200ならば成功
If (httpObj.Status = 200) Then
s = httpObj.responseText
GetHttp = "" & s
Else
GetHttp = ""
Debug.Print "エラー:" & httpObj.statusCode
End If
End Function
[In]※POSTメソッド
Function PostHttp(url, json) As String
'--------------
'POST実行
'--------------
Dim xmlhttp As Object
Set xmlhttp = CreateObject("msxml2.xmlhttp")
xmlhttp.Open "POST", url, False
xmlhttp.setRequestHeader "Content-Type", "application/json; charset=UTF-8" '送信形式をJSONに設定
xmlhttp.send (json) 'JSONデータをPOST
'--------------
'応答取得
'--------------
Dim retCd As String
retCd = xmlhttp.Status '結果コード取得
If retCd <> 200 Then
Debug.Print "error:" & retCd
Else
Dim retHtml As String
retHtml = StrConv(xmlhttp.responseBody, vbUnicode, 1041) '結果HTML取得:戻り値JSON
PostHttp = retHtml
End If
End Function
3-2.JSON解析
JSON解析は下記の通りです。
[In]
' JSON文字列からキーkeyを取り出す --- (*6)
Function GetJsonKey(JsonStr, key, enc) As String
Dim d As Object
If JsonStr = "" Then
GetJsonKey = ""
Exit Function
End If
Set d = CreateObject("htmlfile")
d.Write "<meta http-equiv='X-UA-Compatible' content='IE=8' />"
' JavaScriptの関数を定義 --- (*7)
d.Write "<script>" & _
"document.getjson = function(s, key, enc){" & _
"var vals = eval('('+s+')');" & _
"if (enc) { return JSON.stringify(vals[key]);}" & _
"else { return vals[key] }}" & _
"</script>"
' JavaScriptの関数を呼び出す --- (*8)
GetJsonKey = d.getjson(JsonStr, key, enc)
End Function
3-3.GET/POST処理
上記の通り下記思想でコードを作成しました。
【プロシージャの思想】
●Fast API側にAPIのタイトルを設定したのでGETメソッドで取得する
●Fast API側の戻り値のKey="label"のためJSON解析には"label"を渡す
●POSTするデータはJSON形式のためごり押しでJSONを記載
●指定セルから値を取得して指定セルに値を出力させる
[In]GETメソッドでタイトル取得
Sub FastAPIのタイトル()
Dim api As String
Dim res As String
' APIのURL(Fast APIのroot) --- (*1)
api = "http://127.0.0.1:8000/"
' URLにアクセス --- (*2)
res = GetHttp(api)
' シートに設定 --- (*3)
Sheet1.Range("B9").Value = res
End Sub
[In]POSTメソッドで戻り値取得・解析
Sub Irisデータ取得()
'-----------------
'リクエスト生成
'-----------------
'URL(必要に応じて変更)
Dim url As String, res As String, result As String
Dim json As String '出力:{"sepal_L": 1,"sepal_W": 1,"petal_L": 1,"petal_W": 1}
Dim sepal_L As Double, sepal_W As Double, petal_L As Double, petal_W As Double '変数:データ型浮動小数点
url = "http://127.0.0.1:8000/info" 'Fast-APIのPostエンドポイント
'入力値を作成
sepal_L = Range("A6").Value
sepal_W = Range("B6").Value
petal_L = Range("C6").Value
petal_W = Range("D6").Value
'POSTするパラメータをJSON形式に変更
json = "{""sepal_L"": " & sepal_L & "," & _
"""sepal_W"": " & sepal_W & "," & _
"""petal_L"": " & petal_L & "," & _
"""petal_W"": " & petal_W & "}"
res = PostHttp(url, json)
result = GetJsonKey(res, "label", False)
Sheet1.Range("B10").Value = result
End Sub
3-4.体裁の調整
後は使いやすいように体裁を調整しました。
【Excelの体裁】
●入力表・出力表を作成
●作成したプロシージャをボタンに割り当て
●POSTした戻り値が整数のためラベルを割り当てる関数をB11に追加
[セル:B11]
=IF(B10=0,"setosa",IF(B10=1,"versicolor",IF(B10=2,"virginica","該当なし")))
4.統合および実行
1、2で作成したAIモデル.plkとFast APIを同じディレクトリにします。

次にFast APIを立ち上げURL="http://127.0.0.1:8000"を確認します。
[Terminal]
uvicorn fastapi_iris:app --reload

後はExcelで設定したプロシージャを処理するだけで値が抽出できます。

5.注意点
Fast APIの記事で書きましたが、常時は自分のPCで開発しているため問題ないですが、別PCからサーバーのようにアクセスする場合はFast APIの立ち上げはhost ip addressの設定が必要です。
またAPIのエンドポイントは"127.0.0.1"ではなく接続先のipアドレスに変更が必要となります。
[Terminal]
uvicorn note_fastapi:app --reload --host 0.0.0.0 --port 8000
参考資料:Excel-VBA
あとがき
2021年にこういうのが欲しくて探したけど見当たらなかったので多分初出だと思う。Python側は問題ないと思うけどExcelの関数やJSON形式はもっと美しく書けると思うけどスキル不足で今は断念。普段AIモデルはXGBoostを使用しているのでそれでもいけるか試してみます。2022年2月追記:XGBoostのモデルを用いた実装は問題なし(完了)