SPSS Modeler18.5の新機能の ネイティブ Python APIのテストをしてみました。これは、「拡張ノード」のなかでpandasを使ってModelerのデータを読んだり書いたりすることができる機能です。
この記事ではネイティブ Python APIで「拡張モデル」が、どのように利用可能かをテストしてみました。
マニュアルにサンプル・ストリームがついていましたので、これを修正してテストしてみています。
サンプルストリームは以下です。
- テスト環境
- Windows 11 64bit
- Modeler 18.5
- Python 3.10.7
拡張モデル
OneClassSVMのモデル作成とスコアリング
あらかじめ、以下のコマンドを管理者権限で実行してscikit-learnを導入しておきます。
"C:\Program Files\IBM\SPSS\Modeler\18.5\python_venv\Scripts\python.exe" -m pip install scikit-learn
「Python」を選択し、「Python model building Syntax」の欄と「Python model scoring Syntax」の欄に以下のスクリプトを記入します。
モデルの作成(Python model building Syntax)
###Use this code to import data from Modeler to Python
# ネイティブ Python APIのパッケージ
import modelerpy
import numpy
from sklearn import svm
#前のノードからデータの読み込み
modelerData = modelerpy.readPandasDataframe()
#Modelerのデータモデルを取得
#modelerDataModel = modelerpy.getDataModel()
modelerModel = None
modelName = None
# build model here
# モデルの作成
#カテゴリ変数をLabelEcodingして数値化、numpyの配列化
encoded_data = modelerData.replace({'Sex': {'M': 0, 'F': 1}, 'BP': {'HIGH':3, 'NORMAL':2, 'LOW':1}, 'Cholesterol': {'HIGH':3, 'NORMAL':2}, 'Drug':{'drugA':1, 'drugB':2, 'drugC':3, 'drugX':4, 'drugY':5}})
training_data = encoded_data.to_numpy()
#OneClassSVMでモデル作成。教師なし学習
modelerModel = svm.OneClassSVM()
modelerModel.set_params(nu=0.1)
modelerModel.fit(training_data)
#モデルをストリーム内に保存
modelName = 'svmmodel'
modelerpy.saveModel(modelerModel, modelName)
「モデルの作成(Python model building Syntax)」のスクリプトを解説します。
まず、modelerpy.readPandasDataframe()で前ノードからのデータを読み込んでいます。
前ノードのデータから、カテゴリ変数の「Sex」「BP」「Cholesterol」「Drug」をLabelEcodingして数値化し、numpyの配列化して学習データを準備しています。
encoded_data = modelerData.replace({'Sex': {'M': 0, 'F': 1}, 'BP': {'HIGH':3, 'NORMAL':2, 'LOW':1}, 'Cholesterol': {'HIGH':3, 'NORMAL':2}, 'Drug':{'drugA':1, 'drugB':2, 'drugC':3, 'drugX':4, 'drugY':5}})
training_data = encoded_data.to_numpy()
OneClassSVMでモデル作成を作成しています。ここは普通のscikitlearnのモデリングです。
modelerModel = svm.OneClassSVM()
modelerModel.set_params(nu=0.1)
modelerModel.fit(training_data)
モデルの名前をつけて、saveModelのAPIでストリーム内に保存しています。
Python for Sparkでは、scikitlearnのモデルはストリーム内に保存できなかったために、どこかのフォルダにpickleで保存していましたが、そういう作業が不要です。
modelName = 'svmmodel'
modelerpy.saveModel(modelerModel, modelName)
モデルのスコアリング(Python model scoring Syntax)
###Use this code to transfer data between Modeler and Python
# ネイティブ Python APIのパッケージ
import modelerpy
import numpy
from sklearn import svm
field_outlier = 'Outlier'
field_dist_hp = 'DIST_HP'
#データモデルの参照時
if modelerpy.isComputeDataModelOnly():
modelerDataModel = modelerpy.getDataModel()
outputDataModel = None
#Compute output data model here
#出力データモデルの設定。スコアリング結果を出力するOutlier列とDIST_HP列を追加
outputDataModel = modelerDataModel
outputDataModel.addField(modelerpy.Field(field_outlier, "real", measure="flag"))
outputDataModel.addField(modelerpy.Field(field_dist_hp, "real", measure="continuous"))
modelerpy.setOutputDataModel(outputDataModel)
#データ出力時
else:
modelerData = modelerpy.readPandasDataframe()
modelName = None
#モデルをストリームから読み出し
modelName = 'svmmodel'
modelerModel = modelerpy.loadModel(modelName)
outputData = None
#データの出力
outputData = modelerData
#カテゴリ変数をLabelEcodingして数値化、numpyの配列化
encoded_data = modelerData.replace({'Sex': {'M': 0, 'F': 1}, 'BP': {'HIGH':3, 'NORMAL':2, 'LOW':1}, 'Cholesterol': {'HIGH':3, 'NORMAL':2}, 'Drug':{'drugA':1, 'drugB':2, 'drugC':3, 'drugX':4, 'drugY':5}})
score_data = encoded_data.to_numpy()
#スコアリング
outlier = modelerModel.predict(score_data)
dist_hp = modelerModel.decision_function(score_data)
outputData[field_outlier] = outlier
outputData[field_dist_hp] = dist_hp
#データの出力
modelerpy.writePandasDataframe(outputData)
「モデルのスコアリング(Python model scoring Syntax)」のスクリプトを解説します。
以下でスコアリングで追加される列名を定義しています。
field_outlier = 'Outlier'
field_dist_hp = 'DIST_HP'
大きくはデータモデルの処理とデータ自体の処理の二つに分かれています。
処理の分岐
「データモデルの処理」と「データ自体の処理」の分岐を以下のif文で行っています。
if modelerpy.isComputeDataModelOnly()
modelerpy.isComputeDataModelOnly():のif文は、後続のノードがデータモデルを参照する場合に実データを用意しなくていいための分岐になります。Modelerは、後続のノードを接続した際に前のノードのデータモデルを参照します。例えば「フィルター」ノードを接続した場合にはデータは必要ありませんが、「列名」の情報は必要になります。このif文をなくしても動作はしますが、列名などのデータモデルを参照したいだけなのにデータの読み込みが行われてしまうため、無駄な処理が動いてしまいます。
なお、このif文は、「拡張の変換」ノードと「拡張モデル」ノードといった後続ノードが必要なノードでも、同様に使われます。
データモデルの処理
modelerDataModel = modelerpy.getDataModel()で前のノードからデータモデルを取得しています。
以下でスコアリングで追加される列のメタデータをaddFieldで追加しています。
outputDataModel = modelerDataModel
outputDataModel.addField(modelerpy.Field(field_outlier, "real", measure="flag"))
outputDataModel.addField(modelerpy.Field(field_dist_hp, "real", measure="continuous"))
modelerpy.setOutputDataModel(outputDataModel)で後続ノードに渡すデータモデルを設定しています。
スコアリングの処理
modelerpy.readPandasDataframe()で前ノードからのデータを読み込んでいます。
以下で、ストリーム内に保存したOneClassSVMのモデルを読み出しています。
modelName = 'svmmodel'
modelerModel = modelerpy.loadModel(modelName)
以下で、前ノードのデータから、カテゴリ変数をLabelEcodingして数値化、numpyの配列化してスコアリングデータを準備しています
encoded_data = modelerData.replace({'Sex': {'M': 0, 'F': 1}, 'BP': {'HIGH':3, 'NORMAL':2, 'LOW':1}, 'Cholesterol': {'HIGH':3, 'NORMAL':2}, 'Drug':{'drugA':1, 'drugB':2, 'drugC':3, 'drugX':4, 'drugY':5}})
score_data = encoded_data.to_numpy()
以下で、スコアリングを行っています。
predictで外れ値化どうかのスコアを算出し、decision_functionで距離のスコアを算出しています。
outlier = modelerModel.predict(score_data)
dist_hp = modelerModel.decision_function(score_data)
outputData[field_outlier] = outlier
outputData[field_dist_hp] = dist_hp
modelerpy.writePandasDataframe(outputData)で後続のノードにデータを出力しています。
モデリングノードの実行結果
モデリングノードを実行すると、以下のように、「拡張モデル」のスコアリングノードが自動生成されます。また、「シンタックス」には「拡張モデル」のモデリングノードの「Python model scoring Syntax」に記述したPythonスクリプトがそのままコピーされます。
一応「編集」は可能ですが、「拡張モデリング」ノードとコードが食い違っていると後で混乱すると思いますので、開発・テスト用と考えるのがよいと思います。
スコアリングノードの実行結果
「テーブル」ノードを接続し、実行すると、スコアリング結果が「Outlier」、「DIST_HP」の列に書き出されていることがわかります。