Analytics Publisherによるデータを使用した機械学習モデルの構築 (2025/07/01)
Analytics Publisherによるデータを使用した機械学習モデルの構築 (2025/07/01)
投稿者: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())
カテゴリデータ
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」)が含まれています。
結果を分析する
9. 次のステップでは、混同行列、スコア、分類レポートなどを通じて結果を分析します。この OML ノートブックの最後の段落として、Conda 環境を非アクティブ化します。
%conda
deactivate
Conda environment 'conda' deactivated
まとめ
Oracle Machine LearningとAutonomous Databaseは、データベースのパワーをフルに活用し、Python(OML4Py経由)と組み合わせた堅牢な機械学習環境を提供します。Autonomous Databaseに取り込まれた外部データは、OML Notebooksを利用してSQL、R、Pythonからアクセスできるデータベース表に簡単に変換できます。
コメント
コメントを投稿