5.7.6.2 協調フィルタリングの概要および例

協調フィルタリング(ソーシャル・フィルタとも呼ばれる)は、他の人の推奨を使用して情報をフィルタリングします。協調フィルタリングは、似た好みを持つ他の人の購入内容に基づいて購入を推奨するシステムで広く使用されます。

次の例では、SQLベースの協調フィルタリング分析を示します。

例5-21 協調フィルタリングの設定および計算

この例では、SQLベースの協調フィルタリングの使用方法、具体的には、行列因子分解を使用した顧客への電話ブランドのお薦め方法を示します。この例では、データベース内に「PHONES」というグラフが存在すると想定しています。このグラフ例には、顧客頂点と品目頂点、および一部の顧客頂点を他の一部の品目頂点にリンクする「評価」ラベルを持つエッジが含まれています。評価ラベルには、特定の顧客(エッジOUT頂点)が、指定された商品(エッジIN頂点)に割り当てた評価に対応する数値があります。

次の図は、このグラフを示しています。

図5-1 協調フィルタリング用のPhonesグラフ

図5-1の説明が続きます
「図5-1 協調フィルタリング用のPhonesグラフ」の説明
次のコードは、行列因子分解アルゴリズムを内部で使用する、SQLベースの協調フィルタリング・アルゴリズムを実行するためのエンドツーエンド・フローを示しています。
set serveroutput on

DECLARE
  wt_l varchar2(32);  -- working tables
  wt_r varchar2(32);
  wt_l1 varchar2(32);
  wt_r1 varchar2(32);
  wt_i varchar2(32);
  wt_ld varchar2(32);
  wt_rd varchar2(32);
  edge_tab_name    varchar2(32) := 'phonesge$';
  edge_label       varchar2(32) := 'rating';
  rating_property  varchar2(32) := '';
  iterations       integer      := 100;
  min_error        number       := 0.001;
  k                integer      := 5;
  learning_rate    number       := 0.001;
  decrease_rate    number       := 0.95;
  regularization   number       := 0.02;
  dop              number       := 2;
  tablespace       varchar2(32) := null;
  options          varchar2(32) := null; 
BEGIN

  -- prepare
  opg_apis.cf_prep(edge_tab_name,wt_l,wt_r,wt_l1,wt_r1,wt_i,wt_ld,wt_rd);
  dbms_output.put_line('working table wt_l ' || wt_l);
  dbms_output.put_line('working table wt_r ' || wt_r);
  dbms_output.put_line('working table wt_l1 ' || wt_l1);
  dbms_output.put_line('working table wt_r1 ' || wt_r1);
  dbms_output.put_line('working table wt_i ' || wt_i);
  dbms_output.put_line('working table wt_ld ' || wt_ld);
  dbms_output.put_line('working table wt_rd ' || wt_rd);

  -- compute
  opg_apis.cf(edge_tab_name,edge_label,rating_property,iterations,
              min_error,k,learning_rate,decrease_rate,regularization,dop,
              wt_l,wt_r,wt_l1,wt_r1,wt_i,wt_ld,wt_rd,tablespace,options);
END;
/
このフローは、後ほど計算に渡される一時作業表の作成で開始されます。計算の最後に、例は次の出力を生成する可能性があります。作業表の名前が指定されていない場合は、準備ステップによって自動的に一時表名が生成されて作成される点に注意してください。一時作業表の名前にはセッションIDが使用されるので、出力はおそらく異なります。
working table wt_l    "PHONESGE$$CFL57"
working table wt_r    "PHONESGE$$CFR57"
working table wt_l1    "PHONESGE$$CFL157"
working table wt_r1    "PHONESGE$$CFR157"
working table wt_i    "PHONESGE$$CFI57"
working table wt_ld    "PHONESGE$$CFLD57"
working table wt_rd    "PHONESGE$$CFRD57"

PL/SQL procedure successfully completed.

例5-22 協調フィルタリング:中間エラーの検証

作業表内のデータがまだ削除されていないかぎり、すべての計算の最後に次の問合せを使用してアルゴリズムの現在のエラーを確認できます。次のSQL問合せは、協調フィルタリング・アルゴリズムの現在の実行の中間エラーを取得する方法を示しています。

SELECT /*+ parallel(48) */ SQRT(SUM((w1-w2)*(w1-w2) + 
              <regularization>/2 * (err_reg_l+err_reg_r))) AS err 
  FROM <wt_i>;

正則化パラメータと作業表名(パラメータwt_i)は、OPG_APIS.CFアルゴリズムを実行しているときに使用される値に応じて置き換える必要があることに注意してください。前述の例では、次のように、<regularization>を0.02に置き換え、<wt_i>を「PHONESGE$$CFI149」に置き換えます。

SELECT /*+ parallel(48) */ SQRT(SUM((w1-w2)*(w1-w2) + 0.02/2 * (err_reg_l+err_reg_r))) AS err 
  FROM "PHONESGE$$CFI149";

この問合せでは、次の出力が生成される可能性があります。

       ERR
----------
4.82163662

現在のエラーの値が大きすぎるか、協調フィルタリングの行列因子分解の結果から得た予想がまだ有用でない場合は、作業表とこれまでの進展を再利用して、アルゴリズムのさらなる反復を実行できます。次の例は、SQLベースの協調フィルタリングを使用した予測の実行方法を示しています。

例5-23 協調フィルタリング:予測の実行

協調フィルタリング・アルゴリズムの結果が、行列積の2つの因子であるwt_l表とwt_r表に格納されます。協調フィルタリングの予測をする場合、これらの行列因子を使用する必要があります。

このアルゴリズムの一般的なフローでは、OPG_APIS.CF_CLEANUPプロシージャをコールする前に、2つの行列因子を使用して予測を行うことができます。または、後で使用するために、それらを他の表にコピーして永続的なものにすることができます。次の例は、後者の場合を示しています。

DECLARE
  wt_l varchar2(32);  -- working tables
  wt_r varchar2(32);
  wt_l1 varchar2(32);
  wt_r1 varchar2(32);
  wt_i varchar2(32);
  wt_ld varchar2(32);
  wt_rd varchar2(32);
  edge_tab_name    varchar2(32) := 'phonesge$';
  edge_label       varchar2(32) := 'rating';
  rating_property  varchar2(32) := '';
  iterations       integer      := 100;
  min_error        number       := 0.001;
  k                integer      := 5;
  learning_rate    number       := 0.001;
  decrease_rate    number       := 0.95;
  regularization   number       := 0.02;
  dop              number       := 2;
  tablespace       varchar2(32) := null;
  options          varchar2(32) := null; 
BEGIN

  -- prepare
  opg_apis.cf_prep(edge_tab_name,wt_l,wt_r,wt_l1,wt_r1,wt_i,wt_ld,wt_rd);

  -- compute
  opg_apis.cf(edge_tab_name,edge_label,rating_property,iterations,
              min_error,k,learning_rate,decrease_rate,regularization,dop,
              wt_l,wt_r,wt_l1,wt_r1,wt_i,wt_ld,wt_rd,tablespace,options);
  
  -- save only these two tables for later predictions
  EXECUTE IMMEDIATE 'CREATE TABLE customer_mat AS SELECT * FROM ' || wt_l;
  EXECUTE IMMEDIATE 'CREATE TABLE item_mat AS SELECT * FROM ' || wt_r;

  -- cleanup
  opg_apis.cf_cleanup('phonesge$',wt_l,wt_r,wt_l1,wt_r1,wt_i,wt_ld,wt_rd);
END;
/

この例は、次の出力のみを生成します。

PL/SQL procedure successfully completed.

これで行列因子がcustomer_mat表とitem_mat表に保存されたので、次の問合せを使用して、実際の値(以前、「評価」としてグラフに存在していた値)と見積予想(特定の顧客行と品目列での行列の乗算の結果)の間の「エラー」(差分)を確認できます。

次の問合せは、数値IDではなく、頂点のNVARCHARプロパティ(たとえば、nameプロパティ)を返すために、頂点表で結合によってカスタマイズされていることに注意してください。この問合せは、グラフ内のすべての品目頂点に対するすべての顧客頂点のすべての予測を返します。

SELECT /*+ parallel(48) */ MIN(vertex1.v) AS customer, 
                           MIN(vertex2.v) AS item, 
                           MIN(edges.vn) AS real, 
                           SUM(l.v * r.v) AS predicted
FROM PHONESGE$ edges, 
      CUSTOMER_MAT l, 
      ITEM_MAT r, 
      PHONESVT$ vertex1,   
      PHONESVT$ vertex2
WHERE l.k = r.k
  AND l.c = edges.svid(+)
  AND r.p = edges.dvid(+)
  AND l.c = vertex1.vid
  AND r.p = vertex2.vid
GROUP BY l.c, r.p
ORDER BY l.c, r.p  -- This order by clause is optional
;

この問合せでは、次のような出力が生成される可能性があります(簡略化するために、一部の行は省略されています)。

CUSTOMER	ITEM		REAL	PREDICTED
------------------------------------------------
Adam		Apple 	 	5	3.67375703
Adam		Blackberry		3.66079652
Adam		Danger			2.77049596
Adam		Ericsson		4.21764858
Adam		Figo			3.10631337
Adam		Google		4 	4.42429022
Adam		Huawei		3	3.4289115
Ben		Apple	  	   	2.82127589
Ben		Blackberry	2	2.81132282
Ben		Danger		3	2.12761307
Ben		Ericsson	3   	3.2389595
Ben		Figo	  	    	2.38550534
Ben		Google		   	3.39765075
Ben		Huawei		    	2.63324582
...
Don		Apple		    	1.3777496
Don		Blackberry	1 	1.37288909
Don		Danger		1 	1.03900439
Don		Ericsson	   	1.58172236
Don		Figo		1	1.16494421
Don		Google			1.65921807
Don		Huawei		1	1.28592648
Erik		Apple		3	2.80809351
Erik		Blackberry	3	2.79818695
Erik		Danger			2.11767182
Erik		Ericsson	3 	3.2238255
Erik		Figo			2.3743591
Erik		Google		3	3.38177526
Erik		Huawei		3	2.62094201

予測結果が準備できたか、アルゴリズムの反復をさらに実行する必要があるかどうかを判断するために一部の行のみを確認する場合は、外部問合せの中に前の問合せをラップできます。次の例では、最初の11個の結果のみが選択されます。

SELECT /*+ parallel(48) */ * FROM (
SELECT /*+ parallel(48) */ MIN(vertex1.v) AS customer, 
                           MIN(vertex2.v) AS item, 
                           MIN(edges.vn) AS real, 
                           SUM(l.v * r.v) AS predicted
FROM PHONESGE$ edges, 
     CUSTOMER_MAT l, 
     ITEM_MAT r, 
     PHONESVT$ vertex1,   
     PHONESVT$ vertex2
WHERE l.k = r.k
  AND l.c = edges.svid(+)
  AND r.p = edges.dvid(+)
  AND l.c = vertex1.vid
  AND r.p = vertex2.vid
GROUP BY l.c, r.p
ORDER BY l.c, r.p
) WHERE rownum <= 11;

この問合せでは、次のような出力が生成される可能性があります。

CUSTOMER	ITEM		REAL	PREDICTED
------------------------------------------------
Adam		Apple 	 	5	3.67375703
Adam		Blackberry		3.66079652
Adam		Danger			2.77049596
Adam		Ericsson		4.21764858
Adam		Figo			3.10631337
Adam		Google		4 	4.42429022
Adam		Huawei		3	3.4289115
Ben		Apple	  	   	2.82127589
Ben		Blackberry	2	2.81132282
Ben		Danger		3	2.12761307
Ben		Ericsson	3   	3.2389595

特定の頂点(顧客、品目、またはその両方)の予測を取得するには、希望するID値によって問合せを制限できます。たとえば、頂点1(顧客)の予測値と頂点105(品目)の予測値を取得するには、次の問合せを使用できます。

SELECT /*+ parallel(48) */ MIN(vertex1.v) AS customer, 
                           MIN(vertex2.v) AS item, 
                           MIN(edges.vn) AS real, 
                           SUM(l.v * r.v) AS predicted
FROM PHONESGE$ edges, 
     CUSTOMER_MAT l, 
     ITEM_MAT r, 
     PHONESVT$ vertex1,   
     PHONESVT$ vertex2
WHERE l.k = r.k
  AND l.c = edges.svid(+)
  AND r.p = edges.dvid(+)
  AND l.c = vertex1.vid 
  AND vertex1.vid = 1 /* Remove to get all predictions for item 105 */
  AND r.p = vertex2.vid 
  AND vertex2.vid = 105 /* Remove to get all predictions for customer 1 */
                        /* Remove both lines to get all predictions */
GROUP BY l.c, r.p
ORDER BY l.c, r.p;

この問合せでは、次のような出力が生成される可能性があります。

CUSTOMER	ITEM		REAL	PREDICTED
------------------------------------------------
Adam		Ericsson		4.21764858