機械学習でお客様が他にも買いそうな商品を予測しようじゃないか


## 概要

このチュートリアルでは、ECサイトなどでの顧客の商品購買履歴から、お客様がセット買いしそうな他の商品を予測する手順を解説していきます。

データはOracle Database Cloudに保存されており、High Performance Edition以上のインスタンスが利用されているという前提です。この環境は[無償のトライアル環境をサインアップすることで入手することができます。](https://cloud.oracle.com/tryit)

このチュートリアルで利用するデモ・データもあらかじめ入っているため、すぐに検証をスタートできます。




## 必要な環境

- Oracle Database Cloud - High Performance Edition以上
- SQL Developer（無償のソフトウェア。この記事ではバージョン4.1.5を利用）




## 機械学習機能を有効化する

#### SQL Developerで対象のPDBに接続する

データベース接続を作成するため、SQL Developerを起動し、左上の緑の＋ボタンをクリックします。
![home.png](https://qiita-image-store.s3.amazonaws.com/0/26079/126e36e6-c207-0567-522c-d12975c472a7.png "home.png")

接続情報を入力するウィンドウが開くので自分のデータベースの接続情報を入力します。
＊Oracle Cloud Developersの勉強会では、あらかじめデータベース情報が別途提供されます。当日参加者の皆様に送られるメールを参照ください。
![connection form filled.png](https://qiita-image-store.s3.amazonaws.com/0/26079/0eff1180-5436-03c9-a34a-b4198c2ffb62.png "connection form filled.png")

入力が完了したまず「テスト」ボタンをクリックしてください。しばらくしてエラーが表示されなければOKです。「保存」ボタンをクリックして接続情報を保存し、ポップアップウィンドウを閉じます。

左サイドバーに作成したデータベース接続があるはずです。
![connection_created.png](https://qiita-image-store.s3.amazonaws.com/0/26079/51e54303-4f88-9373-3427-d53ae09da7b1.png "connection_created.png")


#### Data Minerタブを開き、必要なテーブル一式をインストールする（初回のみ）

ナビゲーションメニューからツール > Data Miner > 表示を選択します。
![show_data_miner.png](https://qiita-image-store.s3.amazonaws.com/0/26079/d63d1b92-6b8e-12ba-5a8c-bca4a7d83f4d.png "show_data_miner.png")

すると左のサイドバーにData Minerタブが表示され、そのタブに切り替わります。
![data_miner_tab_opend.png](https://qiita-image-store.s3.amazonaws.com/0/26079/d41f2ddd-57c5-0ec2-180d-246ad30f274a.png "data_miner_tab_opend.png")

左上の緑の＋ボタンをクリックし、分析に利用するデータベース接続を選択します。今回選択するのは先ほど作成したデータベース接続です。選択したらOKをクリックします。
![data_miner_tab_opend.png](https://qiita-image-store.s3.amazonaws.com/0/26079/10d106ed-b848-b567-fd20-79e812fe82e8.png "data_miner_tab_opend.png")
![select connection.png](https://qiita-image-store.s3.amazonaws.com/0/26079/c8be33dc-5723-a5c2-1ce1-851464b36ce7.png "select connection.png")

Data Minerタブの接続にデータベース接続が追加されるはずです。次にその接続を右クリックしてプロジェクトを作成します。任意のプロジェクト名を入力してください。
![new_projecct.png](https://qiita-image-store.s3.amazonaws.com/0/26079/75d05175-020b-59fa-d209-0d4da9474abb.png "new_projecct.png")

作成されたプロジェクトを右クリックしてワークフローを作成します。ワークフローとは分析をおこなう際に作業台のようなものです。こちらも任意のワークフロー名を入力してください。
![new_workflow.png](https://qiita-image-store.s3.amazonaws.com/0/26079/11b5889f-ab15-c2e1-3a9f-3d5481aac7ae.png "new_workflow.png")



## 購買履歴から分析モデルを作成する

#### 分析対象データを指定する

購買履歴におけるアソシエーション分析に必ず必要になるのは **トランザクションID（注文を識別するID）** と **アイテムID（商品を識別するID）** です。この情報が含まれるテーブルをデータソースとして指定します。今回はこれらの情報はDEMO_ORDER_ITEMSテーブルに格納されています。このテーブルには注文された商品が商品ごとに1レコードとして記録されています。

例えば牛乳とシリアルを同時に注文した場合、牛乳を買った記録として1レコード、シリアルを買った記録として1レコードの合計2レコードがDEMO_ORDER_ITEMSに保存されます。そのとき、この2つのレコードは同じトランザクションIDを持ちます。なぜなら同じ注文（トランザクション）だからです。

また、DEMO_ORDER_ITEMSはアイテムIDは保持しているものの、その商品の詳細情報（商品名、価格など）は持ち合わせていません。これは分析において必須ではありませんが、少なくとも商品名はわかった方が分析結果がグンと見やすくなります。この商品の詳細情報はDEMO_PRODUCT_INFOテーブルに保存されており、DEMO_ORDER_ITEMSテーブルとは参照関係にあります。今回はこの2つのテーブルを分析対象として指定します。

右ペインの「データ」パレットから「データソース」を選択してワークスペースにドラッグ＆ドロップし、上記2つのテーブルを順次加えていきます。
![a0978a19-8ac3-5369-6264-a697bffada49.png](https://qiita-image-store.s3.amazonaws.com/0/26079/139e5d1a-3dcd-1fa0-2b6f-32548afa87c4.png "a0978a19-8ac3-5369-6264-a697bffada49.png")

これで分析の準備が整いました。


#### 分析対象データ（複数テーブル）を結合する

これはアソシエーション分析に限りませんが、分析を行う際に対象データは1つのテーブルにまとめる必要があります。今回は2つのテーブルが存在しますがこれらを1つに結合します。

商品の詳細情報はDEMO_PRODUCT_INFOテーブルに保存されており、DEMO_ORDER_ITEMSの各レコードはPRODUCT_IDでDEMO_PRODUCT_INFO内の商品情報を参照しています。よってPRODUCT_IDを結合条件として指定し、2つのテーブルを結合します。

まず右ペインの「変換」パレットから「結合」を選択してワークスペースにドラッグ＆ドロップします。
![スクリーンショット_2016-10-19_17_56_11.png](https://qiita-image-store.s3.amazonaws.com/0/26079/11991f21-623e-f217-34e1-81395748b164.png "スクリーンショット_2016-10-19_17_56_11.png")


そして各データソースを右クリックし、「接続」メニューを選択します。すると別ノードと接続するための線が引っ張れるようになるので、これを「結合」ノードに接続していきます。
![スクリーンショット 2016-10-19 17.59.10.png](https://qiita-image-store.s3.amazonaws.com/0/26079/af6d88c7-1d8d-3d28-7777-2cbdfc3d0083.png "スクリーンショット 2016-10-19 17.59.10.png")


次に「結合」ノードを右クリックして「編集」メニューを選択します。ポップアップしたウィンドウで結合ルールを設定できます。緑のプラスボタンをクリックして結合ルールを追加します。

![結合ルール追加.png](https://qiita-image-store.s3.amazonaws.com/0/26079/723d83e7-d7f5-9231-0292-ae5cc7acbb2f.png "結合ルール追加.png")

DEMO_ORDER_ITEMSとDEMO_PRODUCT_INFOとの結合ルールを追加します。前述の通り、両テーブルはPRODUCT_IDを通じて参照関係が張られているのでこのPRODUCT_IDを指定して結合ルールを追加します。
![スクリーンショット 2016-10-19 18.01.32.png](https://qiita-image-store.s3.amazonaws.com/0/26079/3da98f4c-ca43-a83a-6108-722f31504702.png "スクリーンショット 2016-10-19 18.01.32.png")


これでOKボタンをクリックすれば結合が完成です。
結合したデータは「結合」ノードを右クリックして「データの表示」メニューを選択すると確認できます。


#### 分析モデルを設定する

分析対象のデータの準備が完了したので、次に分析モデルを設定します。今回はアソシエーション分析をおこなうわけですが、アソシエーション分析においては **トランザクションID** と **アイテムID** の設定が必要です。

トランザクションIDはセット買いするアイテムが入ったバスケットを識別するIDです。これはECサイトにおいては多くの場合ショッピングカートを意味するでしょう。

> 逆に自動車販売など、額が大きい商品においては顧客IDがトランザクションIDになることも考えられます。この場合、一度の買い物ではなく、数年単位といった長期でみたときにどんな商品を購入していくか、という分析になるでしょう。

アイテムIDはバスケットに入れる商品のIDを意味しています。

まず右ペインの「モデル」パレットから「アソシエーション」を選択してワークスペースにドラッグ＆ドロップします。「相関構築」ノードが追加されるはずです。
![スクリーンショット_2016-10-19_18_04_03.png](https://qiita-image-store.s3.amazonaws.com/0/26079/00083fba-fe2a-52ac-c67c-442fbf65e97e.png "スクリーンショット_2016-10-19_18_04_03.png")



そして「結合」ノードを右クリックして接続メニューを選択し、「相関構築」ノードと接続します。するとアソシエーション分析に必要な設定をおこなうためのウィンドウがポップアップします。ここで下記のように設定します。

- トランザクションID: ORDER_ID
- アイテムID: PRODUCT_ID
- 値: PRODUCT_NAME 

値とはアイテムのラベル（名称）です。オプションですが指定しておいた方が分析結果がわかりやすくなります。

そしてモデル設定の欄に今回作成する分析モデルの名称が仮決めされているのですが、これをわかりやすいように「OSUSUME」に変更しておきます。
![スクリーンショット_2016-10-02_10_47_01.png](https://qiita-image-store.s3.amazonaws.com/0/26079/2f785f68-aeda-82c4-2b58-fe949a451137.png "スクリーンショット_2016-10-02_10_47_01.png")

これでOKボタンをクリックすれば分析モデルの設定が完了です。「相関構築」ノードを選択し、緑の再生ボタンをクリックすれば分析が開始されます。
![スクリーンショット_2016-10-19_18_06_08.png](https://qiita-image-store.s3.amazonaws.com/0/26079/2fd022d9-aac7-a479-65d0-e93abf84cade.png "スクリーンショット_2016-10-19_18_06_08.png")


分析が完了したら、「相関構築」ノードを右クリックし、「モデルの表示」 > 「OSUSUME」を選択すると分析結果を確認することができます。
![スクリーンショット 2016-10-02 11.12.47.png](https://qiita-image-store.s3.amazonaws.com/0/26079/fff586fb-466a-ba0d-8a1f-3f2db1cd4c6f.png "スクリーンショット 2016-10-02 11.12.47.png")





## セット買いしそうな商品を予測する

#### PL/SQLパッケージ：GET_ASSOCIATION_RULES

ではECサイトで顧客がバスケットに商品を入れた際、セット買いしそうな商品を予測するにはどうすればよいでしょうか。先ほどの分析結果にはすべての購買パターンが網羅されているので、この結果を検索すれば答えは出るはずです。

検索方法は、顧客がバスケットに入れた商品が「先行情報（antecedent）」になりますので、そのときの「結果（consequent）」を取得し、かつその確度が高いものを上位から何件か抽出すればよいはずです。

こういった条件を指定する検索機能が「DBMS_DATA_MINING.GET_ASSOCIATION_RULES」というPL/SQLパッケージにまとめられています。

**DBMS_DATA_MINING.GET_ASSOCIATION_RULESの構文**

```SQL
DBMS_DATA_MINING.GET_ASSOCIATION_RULES (
   model_name            IN VARCHAR2, // モデル名
   topn                  IN NUMBER                 DEFAULT NULL, // 取得するルールの件数
   rule_id               IN NUMBER (38)            DEFAULT NULL, // ルールのID
   min_confidence        IN NUMBER                 DEFAULT NULL, // 最低確度
   min_support           IN NUMBER                 DEFAULT NULL, // ルールが示すアイテム組み合わせパターンの最低出現率
   max_rule_length       IN NUMBER (38)            DEFAULT NULL, // ルールに含まれるアイテムの最大件数
   min_rule_length       IN NUMBER (38)            DEFAULT NULL, // ルールに含まれるアイテムの最小件数
   sort_order            IN ORA_MINING_VARCHAR2_NT DEFAULT NULL, // ソートとする列　カンマ区切りで複数指定可能　各カラムにASC or DESCを指定可能　ORA_MINING_VARCHAR2_NT()でそれらを括る
   antecedent_items      IN DM_ITEMS               DEFAULT NULL, // 先行情報
   consequent_items      IN DM_ITEMS               DEFAULT NULL, // 結果
   min_lift              IN NUMBER                 DEFAULT NULL)
 RETURN DM_RULES PIPELINED;
```


**DBMS_DATA_MINING.GET_ASSOCIATION_RULESの実行サンプル**

今回の場合、まずモデル名に作成した「OSUSUME」を指定します。そして確度の高い上位3件のルールを取得することにします。顧客は商品ID「1」の商品をバスケットに入れたとします。

この条件で顧客がセット買いしそうな商品を検索するには下記のようにGET_ASSOCIATION_RULESを実行します。

```SQL
DBMS_DATA_MINING.GET_ASSOCIATION_RULES(
	'OSUSUME', 
	3,
	null,
	null,
	null,
	1,
	null,
	ORA_MINING_VARCHAR2_NT('RULE_CONFIDENCE DESC'),
	DM_ITEMS(DM_ITEM('PRODUCT_ID', 1, null, null))
)
```

GET_ASSOCIATION_RULESはDM_RULESというオブジェクトで結果を返します。このオブジェクトはそのままでは見れないので、まずTABLE()関数でテーブルに変換します。そしてそのテーブルからSELECT文で結果を問い合わせる形にします。

**GET_ASSOCIATION_RULESをSQLの中に埋め込み、テーブル形式で結果を取得する**

```SQL
SELECT
	*
FROM
    TABLE(DBMS_DATA_MINING.GET_ASSOCIATION_RULES(
        'OSUSUME', 
        3,
        null,
        null,
        null,
        1,
        null,
        ORA_MINING_VARCHAR2_NT('RULE_CONFIDENCE DESC'),
        DM_ITEMS(DM_ITEM('PRODUCT_ID', 1, null, null))
    ))
```

下記のような結果が返ってきます。

RULE_ID | ANTECEDENT | CONSEQUENT | RULE_SUPPORT | RULE_CONFIDENCE | RULE_LIFT | ANTECEDENT_SUPPORT | CONSEQUENT_SUPPORT | NUMBER_OF_ITEMS
--:|--:|--:|--:|--:|--:|--:|--:|--:|
1 | [unsupported data type] | [unsupported data type] | .5 | 1 | 1.66666666666666666666666666666666666667 | .5 | .6 | 1
3 | [unsupported data type] | [unsupported data type] | .4 | .8 | 1.33333333333333333333333333333333333333 | .5 | .6 | 1
7 | [unsupported data type] | [unsupported data type] | .3 | .6 | 1.2 | .5 | .5 | 1

ANTECEDENTには条件で指定した商品ID「1」の商品が、そしてCONSEQUENTにはセット買いする可能性の高い商品が入っているのですが、この値はDM_ITEMSというオブジェクトになっているため、そのままでは表示できません。

この結果は先ほどと同様、TABLE()関数を用いて一旦テーブル形式に変換し、そして中の値を取得します。

**入れ子になっているCONSEQUENTオブジェクトをテーブル形式に変換して結果を取得する**

先ほどのSQLの末尾にTABLE()関数を加えて、展開したいオブジェクトを指定します。そして展開したオブジェクトはプロパティを列としてSQL文で指定することができます。

```sql
SELECT
    cons.attribute_name,
    cons.attribute_subname,
    cons.attribute_str_value,
    round(rules.rule_confidence * 100) || '%' buy_probability
FROM
    TABLE(DBMS_DATA_MINING.GET_ASSOCIATION_RULES(
        'OSUSUME', 
        3,
        null,
        null,
        null,
        1,
        null,
        ORA_MINING_VARCHAR2_NT('RULE_CONFIDENCE DESC'),
        DM_ITEMS(DM_ITEM('PRODUCT_ID', 1, null, null))
    )) rules,
    TABLE(CONSEQUENT) cons
```

下記のような結果が返ってきます。

ATTRIBUTE_NAME | ATTRIBUTE_SUBNAME | ATTRIBUTE_STR_VALUE | BUY_PROBABILITY
--:|--:|--:|--:|
PRODUCT_ID | 2 | Trousers | 100%
PRODUCT_ID | 3 | Jacket | 80%
PRODUCT_ID | 5 | Skirt | 60%

これで商品ID「1」の商品を買った顧客は、Trousers（ズボン）をセット買いする可能性が極めて高い（というか100%）ということがわかります。あとはアプリケーション側でこの結果が好きな機能や画面に利用すれば良いわけです。
 
 
 
 
## 参考情報
 
- [Data Mining Concepts](https://docs.oracle.com/database/121/DMCON/toc.htm)
- [Data Mining User's Guide](https://docs.oracle.com/database/121/DMPRG/toc.htm)
- [PL/SQL Package: DBMS_DATA_MINING Reference](https://docs.oracle.com/database/121/ARPLS/d_datmin.htm#ARPLS192)