Analytics Publisherによるデータを使用した機械学習モデルの構築 (2025/07/01)

Analytics Publisherによるデータを使用した機械学習モデルの構築 (2025/07/01)

https://blogs.oracle.com/machinelearning/post/building-machine-learning-models-using-data-through-analytics-publisher

投稿者:Shankar Duvvuri | Principal Technical Support Engineer

Oracle Autonomous Databaseを使用して機械学習モデルを構築できることはご存知かもしれませんが、Fusion ApplicationsベースのAnalytics Publisher(旧BI Publisher)レポートの出力を使用して構築できることをご存知でしたか?これらのレポートは、Fusionからデータを抽出するための承認された方法の1つであり、財務、注文管理、サプライチェーンなどのさまざまなFusionモジュールからデータを取得できます。これにより、支払いの遅延の追跡、フォローアップが必要な請求書、在庫切れの可能性がある製品など、さまざまなユースケース向けのカスタム機械学習モデルの効果的なデータソースになります。このブログでは、Analytics Publisher(AP)レポートを使用して機械学習モデルを構築するために必要な手順を説明します。 

注 - 前提条件として、 Analytics Publisher のレポート出力にアクセスし、それを Autonomous Database にテーブルとして保存する方法の詳細については、 ブログを参照してください。

APレポートとデータの例の詳細

このレポートは、Oracle Transactional Business Intelligence (OTBI) のサブジェクトエリア「売掛金 - 支払スケジュール リアルタイム」から取得したデータを使用して作成されています(下図参照)。このクエリには追加の計算式と、「Followup_Required」という列が含まれており、この列は各取引について追加のフォローアップが必要かどうかを示します。有効な値は「はい」または「いいえ」です。 

このレポートに使用されているデータは、Vision Corpのデータ(Fusion Applicationsに付属する、架空の企業Vision Corpに基づいた架空のサンプルデータ)を使用したデモポッドから取得されています。実際のレポート出力は10列で構成され、詳細は次のとおりです。

列名説明
トランザクションID取引の一意の識別子
エイジングバケット延滞バケットを「延滞1~30日」、「延滞31~60日」、「延滞61~90日」、または「延滞90日以上」として定義します。
期日からの日数支払期限からの合計日数
残高お支払いいただく残額
予想未回収額ビジネス要件に一致する計算式に基づいて回収されない可能性が高い予想金額
AR_Overdue_1_30_郡    
AR_Overdue_31_60_郡    
AR_Overdue_61_90_郡    
AR_Overdue_90plus_郡
それぞれのバケットに該当する未払い金額。各取引は一意であるため、4つのバケットのうち1つでは金額は0以外となり、残りのバケットでは0となります。
フォローアップ必須この列は、ビジネス要件に基づいた数式を使用して入力され、このトランザクションに関するさらなるフォローアップが必要かどうかを判断します。現在構築している機械学習モデルの目的は、この列の値を予測することです。

この例では、ロジスティック回帰を用いて「Followup_Required」を予測します。「Followup_Required」(ターゲット列)は、主に様々な経過期間バケットの金額値とバケットの種類に基づいて「はい」または「いいえ」に設定されるため、「Transaction_ID」、「Days_since_due」、「Expected_Uncollected_amount」、「Amount_Remaining」列は削除します。残りの列は予測の特徴量として使用されます。 

以下は、Analytics Publisher でレポートを作成する際に使用したサンプルクエリです。ご自身の環境で試してみたい方は、ぜひご覧ください。このクエリは、OTBI サブジェクトエリア「売掛金 - 支払スケジュール リアルタイム」に基づいている点にご注意ください。ご覧のとおり、未払金額と「Aging_Bucket」(現在未払金額がどのバケットに該当するか)に基づいて、「Followup_Required」列が追加されています。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
SELECT "Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Transaction ID" Transaction_ID,
   CASE WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 1 AND 30 THEN 'Overdue 1-30 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 31 AND 60 THEN 'Overdue 31-60 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 61 AND 90 THEN 'Overdue 61-90 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE)>90 THEN 'Overdue 90+ Days' ELSE 'Other (Error)' END Aging_Bucket,
   TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) Days_since_due,
   "Receivables - Payment Schedules Real Time"."Payment Schedules"."Line Entered Amount Remaining" Amount_Remaining,
   CASE WHEN TIMESTAMPDIFF(SQL_TSI_DAY, "Payment Schedules Details"."Due Date", CURRENT_DATE) BETWEEN 1 AND 30 THEN .01 * "Payment Schedules"."Line Entered Amount Remaining" WHEN TIMESTAMPDIFF(SQL_TSI_DAY, "Payment Schedules Details"."Due Date", CURRENT_DATE) BETWEEN 31 AND 60 THEN .03 * "Payment Schedules"."Line Entered Amount Remaining" WHEN TIMESTAMPDIFF(SQL_TSI_DAY, "Payment Schedules Details"."Due Date", CURRENT_DATE) BETWEEN 61 AND 90 THEN .10 * "Payment Schedules"."Line Entered Amount Remaining" WHEN TIMESTAMPDIFF(SQL_TSI_DAY, "Payment Schedules Details"."Due Date", CURRENT_DATE) > 90 THEN .40 * "Payment Schedules"."Line Entered Amount Remaining" ELSE 0 END Expected_Uncollected_amout,
   case (CASE WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 1 AND 30 THEN 'Overdue 1-30 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 31 AND 60 THEN 'Overdue 31-60 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 61 AND 90 THEN 'Overdue 61-90 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE)>90 THEN 'Overdue 90+ Days' ELSE 'Other (Error)' END) when 'Overdue 1-30 Days' then "Receivables - Payment Schedules Real Time"."Payment Schedules"."Line Entered Amount Remaining" else 0 end AR_Overdue_30_Amount,
   case (CASE WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 1 AND 30 THEN 'Overdue 1-30 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 31 AND 60 THEN 'Overdue 31-60 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 61 AND 90 THEN 'Overdue 61-90 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE)>90 THEN 'Overdue 90+ Days' ELSE 'Other (Error)' END) when 'Overdue 31-60 Days' then "Receivables - Payment Schedules Real Time"."Payment Schedules"."Line Entered Amount Remaining" else 0 end AR_Overdue_3160_Amount,
   case (CASE WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 1 AND 30 THEN 'Overdue 1-30 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 31 AND 60 THEN 'Overdue 31-60 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 61 AND 90 THEN 'Overdue 61-90 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE)>90 THEN 'Overdue 90+ Days' ELSE 'Other (Error)' END) when 'Overdue 61-90 Days' then "Receivables - Payment Schedules Real Time"."Payment Schedules"."Line Entered Amount Remaining" else 0 end AR_Overdue_6190_Amount,
   case (CASE WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 1 AND 30 THEN 'Overdue 1-30 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 31 AND 60 THEN 'Overdue 31-60 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE) BETWEEN 61 AND 90 THEN 'Overdue 61-90 Days' WHEN TIMESTAMPDIFF(SQL_TSI_DAY,"Receivables - Payment Schedules Real Time"."Payment Schedules Details"."Due Date",CURRENT_DATE)>90 THEN 'Overdue 90+ Days' ELSE 'Other (Error)' END) when 'Overdue 90+ Days' then "Receivables - Payment Schedules Real Time"."Payment Schedules"."Line Entered Amount Remaining" else 0 end AR_Overdue_90_Amount
FROM "Receivables - Payment Schedules Real Time"
WHERE
((("Payment Schedules"."Line Entered Amount Remaining" > 0) AND (TIMESTAMPDIFF(SQL_TSI_DAY, "Payment Schedules Details"."Due Date", CURRENT_DATE) > 0) AND ("Payment Schedules Details"."Transaction Type" = 'Invoice')))

使用されたツール 

この例では、Autonomous DatabaseのOMLノートブックと、Oracle Machine Learning for Python(OML4Py)を使用したPython言語のパラグラフを使用しています。Conda環境は、サードパーティ製のライブラリZeepを使用して作成しました。Zeepライブラリは、PythonでSOAPベースのWebサービスを処理するために使用されます。Zeepライブラリの詳細については、こちらのGitHubページをご覧ください。

データの取得と準備

最初のステップとして、前回のブログで詳しく説明したように、zeepライブラリを含むConda環境をダウンロードしてアクティブ化します。詳細については、 「Autonomous Databaseで使用するためのカスタムサードパーティ製PythonおよびRパッケージの発表」ドキュメントを参照してください。ここで使用したサンプルでは、Conda環境は「mypyenv」という名前で作成されました。

1. Analytics Publisher レポートにアクセスするために必要なライブラリをインポートします。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
%conda

download mypyenv --overwrite
activate mypyenv

%python

import requests
from zeep.transports import Transport
from zeep import Client
from zeep import helpers

2. 以下のようにレポートリクエストを定義します。ただし、<server_name>、user_name、passwd は実際の値に置き換えてください。チャンク化の要件と属性形式の詳細については、こちらを参照してください。Fusion Applications における BI Publisher Web サービスのベストプラクティスドキュメントも併せてご覧ください。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
%python

WSDL = "https://<server_name>/xmlpserver/services/ExternalReportWSSService?wsdl"
user_name = "???"
passwd = "***"

bip_report_request = {
    'reportAbsolutePath': '/Custom/ADB_OML/RP_AR_Inv.xdo',
    'sizeOfDataChunkDownload': '10000',
    'byPassCache': 'False',
    'flattenXML': 'False',
    'attributeFormat': 'csv'
}

3. runReport メソッドを呼び出して、次の手順を実行します。

  • request.Session() を通じて有効なセッションを取得します。 
  • ユーザー名とパスワードでセッションを承認しました。 
  • 承認されたセッションを渡してトランスポート オブジェクトを取得します。 
  • WSDL とトランスポート オブジェクトを指定して、bip_client オブジェクトを初期化します。 
  • 上記で宣言した bip_report_request ディクショナリとレポート固有のパラメータ値 (この特定のケースでは必要ありません) を渡して、Analytics Publisher Web サービス ExternalReportWSSService の runReport メソッドを呼び出します。

クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
%python

bip_session = requests.Session()
bip_session.auth = (user_name, passwd)
bip_transport = Transport(session=bip_session)

bip_client = Client(wsdl=WSDL, transport=bip_transport)
bip_rep_result = bip_client.service.runReport(reportRequest=bip_report_request, appParams="")


4. Webサービス呼び出しから得られた結果は、helpers.serialize_objectを使用してPython辞書に変換されます。Python辞書変数bip_rep_result_dictには、Webサービス呼び出しから返された様々なキーと値のペアが格納されます。そのうち、「reportBytes」キーには、エンコードされたバイト形式でレポート出力が格納されます。デコードされたレポート出力は文字列変数rows_strに格納されます。これにより、rows_strには、人間が読めるCSV形式の文字列でレポート出力が格納されます。この段落の最後のステップとして、bip_sessionを終了します。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
%python

bip_rep_result_dict = helpers.serialize_object(bip_rep_result, target_cls=dict)

rows_byte = bip_rep_result_dict['reportBytes']
rows_str = rows_byte.decode()

bip_session.close()
z.show(rows_str)

5. Analytics Publisher から取得したレポート出力を印刷します。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
RANSACTION_ID,AGING_BUCKET,DAYS_SINCE_DUE,AMOUNT_REMAINING,EXPECTED_UNCOLLECTED_AMOUNT,AR_OVERDUE_1_30_AMT,AR_OVERDUE_31_60_AMT,AR_OVERDUE_61_90_AMT,AR_OVERDUE_90PLUS_AMT,FOLLOWUP_REQUIRED
1285059,"Overdue 1-30 Days",26,34698,347,34698,0,0,0,No
1284978,"Overdue 31-60 Days",44,46982,1409,0,46982,0,0,Yes
1284963,"Overdue 61-90 Days",71,86942,8694,0,0,86942,0,Yes
1284954,"Overdue 90+ Days",632,1304130,521652,0,0,0,1304130,Yes
1286115,"Overdue 1-30 Days",15,607719,6077,607719,0,0,0,Yes
1284561,"Overdue 1-30 Days",25,10430,104,10430,0,0,0,No
1286108,"Overdue 90+ Days",554,786969,314788,0,0,0,786969,Yes
1284551,"Overdue 31-60 Days",58,10061,302,0,10061,0,0,Yes
1286101,"Overdue 61-90 Days",63,74140,7414,0,0,74140,0,Yes
1284540,"Overdue 90+ Days",179,4182,1673,0,0,0,4182,Yes
1286092,"Overdue 1-30 Days",22,46982,470,46982,0,0,0,No
1286208,"Overdue 31-60 Days",35,148354,4451,0,148354,0,0,Yes
1284528,"Overdue 61-90 Days",80,4253,425,0,0,4253,0,Yes
1286202,"Overdue 90+ Days",99,154790,61916,0,0,0,154790,Yes
1284517,"Overdue 90+ Days",120,4756,1902,0,0,0,4756,Yes
1284505,"Overdue 1-30 Days",12,2800,28,2800,0,0,0,No
1284494,"Overdue 31-60 Days",45,6385,192,0,6385,0,0,No
1286037,"Overdue 61-90 Days",68,285386,28539,0,0,285386,0,Yes
1284481,"Overdue 90+ Days",96,4852,1941,0,0,0,4852,Yes
1286030,"Overdue 1-30 Days",17,103975,1040,103975,0,0,0,Yes
1284664,"Overdue 1-30 Days",22,761,8,761,0,0,0,No
1286177,"Overdue 1-30 Days",29,30958,310,30958,0,0,0,No

6. レポート出力をPandasのDataFrameに変換し、数行のデータを表示します。完全なレポートには150行あります。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
%python

import pandas as pd
from io import StringIO

inv_df = pd.read_csv(StringIO(rows_str))
z.show(inv_df.head())

DataFrameの内容を表示する

カテゴリデータ

OMLインデータベースアルゴリズムは、カテゴリ変数のエンコードを自動的に処理します。サンプルデータでは、2つの列(「AGING_BUCKET」と「FOLLOWUP_REQUIRED」)が文字列であるため、インデータベースアルゴリズムで使用するためにコード内で特に処理する必要はありません。

トレーニングと予測

7. 次に、以下の手順に従ってモデルを構築し、データベース内アルゴリズムを使用して結果を予測します。 

  • Pandas DataFrame「invdf」を使用してテーブルを作成します。まず、テーブルが存在するかどうかを確認します。存在する場合はテーブルを削除し、Pandas DataFrame「inv_df」を渡してoml.createメソッドを呼び出します。 
  • split関数を使用して、テーブルデータをOMLデータフレーム「data_prep」にロードします。split関数は、トレーニングデータとテストデータの比率を80:20に設定します。 
  • X_train を宣言し、トレーニングセット部分の「FOLLOWUP_REQUIRED」(予測変数のみ)を除くすべての列を保持します。y_train には、トレーニングセット部分の「FOLLOWUP_REQUIRED」(ターゲット列)のみを使用します。test_data を宣言し、テストセット部分のすべてのデータを保持します。
  • 設定変数を辞書型として宣言し、キーを「GLMS_SOLVER」(一般化線形モデル)、値を「dbms_data_mining.GLMS_SOLVER_QR」とします。一般化線形モデルの詳細については、ドキュメントをご覧ください。 
  • olm.glm関数を呼び出す際に「classification」オプションを使用して、線形回帰に関心があることを示します。次に、glm_mod.fitを呼び出してモデルを学習します。
  • モデルを学習した後、スコア関数を呼び出してテストデータセットを用いた予測値を取得します。glm_mod.score を呼び出す際、予測変数には test_data.drop(“FOLLOWUP_REQUIRED”) (ターゲットとなる列を除くすべての列)を使用し、ターゲットには “FOLLOWUP_REQUIRED” のみを使用します。

クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
%python

import oml

try:
    oml.drop('AR_INV')
except:
    pass

oml_ar_inv = oml.create(inv_df, table = 'AR_INV')

data_prep = oml.sync(table = 'AR_INV').split(ratio=(.80, .20), seed = 1)
X_train   = data_prep[0].drop('FOLLOWUP_REQUIRED')
y_train   = data_prep[0]['FOLLOWUP_REQUIRED']
test_data = data_prep[1]

setting = {'GLMS_SOLVER': 'dbms_data_mining.GLMS_SOLVER_QR'}
glm_mod = oml.glm("classification", **setting)
glm_mod = glm_mod.fit(X_train, y_train)
z.show('Score: ' + str(glm_mod.score(test_data.drop('FOLLOWUP_REQUIRED'), test_data[:, ['FOLLOWUP_REQUIRED']])))

suppl_cols = ['AGING_BUCKET', 'AR_OVERDUE_1_30_AMT', 'AR_OVERDUE_31_60_AMT', 'AR_OVERDUE_61_90_AMT', 'AR_OVERDUE_90PLUS_AMT', 'FOLLOWUP_REQUIRED']
z.show(glm_mod.predict(test_data.drop('FOLLOWUP_REQUIRED'), supplemental_cols = test_data[:, suppl_cols]))

8. 大規模なデータセットの場合、トレーニングデータは可能な限り多くのシナリオをカバーする必要があります。これは、トレーニングに提供するデータ量を増やしたり、テストに異なるデータセットを使用したりすることで、モデルのパフォーマンスが変化する可能性があるためです。テスト結果によっては、モデルの調整が必要になる場合があります。このOMLノートブックの段落の最後のステップとして、テストデータと予測データを表示します。変数suppl_colsには、出力に含めるすべての列、テストセット内の「FOLLOWUP_REQUIRED」の元の値、およびモデルによる予測値(「FOLLOWUP_REQUIRED」)が含まれています。

DataFrameで結果を表示

結果を分析する

9. 次のステップでは、混同行列、スコア、分類レポートなどを通じて結果を分析します。この OML ノートブックの最後の段落として、Conda 環境を非アクティブ化します。


クリップボードにコピーされました
エラー: コピーできませんでした
クリップボードにコピーされました
エラー: コピーできませんでした
%conda

deactivate

Conda environment 'conda' deactivated


まとめ

Oracle Machine LearningとAutonomous Databaseは、データベースのパワーをフルに活用し、Python(OML4Py経由)と組み合わせた堅牢な機械学習環境を提供します。Autonomous Databaseに取り込まれた外部データは、OML Notebooksを利用してSQL、R、Pythonからアクセスできるデータベース表に簡単に変換できます。


コメント

このブログの人気の投稿

Oracle Database 19cサポート・タイムラインの重要な更新 (2024/11/20)

Oracle GoldenGate 23aiでMicrosoft Fabricでのオープン・ミラーリングがサポートされるようになりました (2024/11/19)

OCIサービスを利用したWebサイトの作成 その4~Identity Cloud Serviceでサイトの一部を保護 (2021/12/30)