1. 序章

このチュートリアルでは、Javaの SlopeOneアルゴリズムについてすべて学習します。

また、レコメンデーションシステムで使用される機械学習手法である協調フィルタリング(CF)の問題の実装例も示します。

これは、たとえば、特定のアイテムに対するユーザーの関心を予測するために使用できます。

2. 協調フィルタリング

Slope Oneアルゴリズムは、アイテムベースの協調フィルタリングシステムです。 これは、完全にユーザーアイテムのランキングに基づいていることを意味します。 オブジェクト間の類似性を計算する場合、コンテンツ自体ではなく、ランキングの履歴のみがわかります。 この類似性は、データセットに存在しないユーザーとアイテムのペアの潜在的なユーザーランキングを予測するために使用されます。

以下の画像は、特定のユーザーの評価を取得して計算する完全なプロセスを示しています。

最初に、ユーザーはシステム内のさまざまなアイテムを評価します。 次に、アルゴリズムは類似性を計算します。 その後、システムは、ユーザーがまだ評価していないユーザーアイテムの評価を予測します。

協調フィルタリングのトピックの詳細については、ウィキペディアの記事を参照してください。

3. SlopeOneアルゴリズム

Slope Oneは、評価に基づく重要なアイテムベースの協調フィルタリングの最も単純な形式として名付けられました。 同じアイテムを評価したすべてのユーザーからの情報と、同じユーザーによって評価された他のアイテムからの情報の両方を考慮して、類似性マトリックスを計算します。

簡単な例では、ストア内のアイテムのユーザーランキングを予測します。

問題とドメインの単純なJavaモデルから始めましょう。

3.1. Javaモデル

このモデルには、アイテムとユーザーの2つの主要なオブジェクトがあります。 Item クラスには、アイテムの名前が含まれています。

private String itemName;

一方、Userクラスにはユーザー名が含まれています。

private String username;

最後に、データの初期化に使用されるInputDataクラスがあります。 ストアで5つの異なる製品を作成するとします。

List<Item> items = Arrays.asList(
  new Item("Candy"), 
  new Item("Drink"), 
  new Item("Soda"), 
  new Item("Popcorn"), 
  new Item("Snacks")
);

さらに、0.0〜1.0のスケールを使用して、上記の一部をランダムに評価する3人のユーザーを作成します。ここで、0は興味がない、0.5はやや興味がある、1.0は完全に興味があることを意味します。 データの初期化の結果、ユーザーアイテムのランク付けデータを含むMapが取得されます。

Map<User, HashMap<Item, Double>> data;

3.2. 差異と周波数行列

利用可能なデータに基づいて、アイテム間の関係とアイテムの発生数を計算します。 ユーザーごとに、アイテムの評価を確認します。

for (HashMap<Item, Double> user : data.values()) {
    for (Entry<Item, Double> e : user.entrySet()) {
        // ...
    }
}

次のステップでは、アイテムがマトリックスに存在するかどうかを確認します。 これが最初の発生である場合、マップに新しいエントリを作成します。

if (!diff.containsKey(e.getKey())) {
    diff.put(e.getKey(), new HashMap<Item, Double>());
    freq.put(e.getKey(), new HashMap<Item, Integer>());
}

最初のマトリックスは、ユーザーの評価間の差を計算するために使用されます。 その値は正または負の場合があり(評価間の差が負の場合があるため)、Doubleとして保存されます。 一方、頻度はInteger値として保存されます。

次のステップでは、すべてのアイテムの評価を比較します。

for (Entry<Item, Double> e2 : user.entrySet()) {
    int oldCount = 0;
    if (freq.get(e.getKey()).containsKey(e2.getKey())){
        oldCount = freq.get(e.getKey()).get(e2.getKey()).intValue();
    }

    double oldDiff = 0.0;
    if (diff.get(e.getKey()).containsKey(e2.getKey())){
        oldDiff = diff.get(e.getKey()).get(e2.getKey()).doubleValue();
    }
    
    double observedDiff = e.getValue() - e2.getValue();
    freq.get(e.getKey()).put(e2.getKey(), oldCount + 1);
    diff.get(e.getKey()).put(e2.getKey(), oldDiff + observedDiff);
}

以前に誰かがそのアイテムを評価した場合は、頻度カウントを1つ増やします。 さらに、アイテムの評価の平均差をチェックし、新しいobservedDiffを計算します。

oldDiffobservedDiffの合計をアイテムの新しい値として配置していることに注意してください。

最後に、行列内の類似度スコアを計算します。

for (Item j : diff.keySet()) {
    for (Item i : diff.get(j).keySet()) {
        double oldValue = diff.get(j).get(i).doubleValue();
        int count = freq.get(j).get(i).intValue();
        diff.get(j).put(i, oldValue / count);
    }
}

主なロジックは、計算されたアイテムの評価の差をその発生数で割ることです。 そのステップの後、最終的な差分行列を印刷できます。

3.3. 予測

Slope Oneの主要部分として、既存のデータに基づいて欠落しているすべての評価を予測します。 これを行うには、ユーザーアイテムの評価を前の手順で計算された差分行列と比較する必要があります。

for (Entry<User, HashMap<Item, Double>> e : data.entrySet()) {
    for (Item j : e.getValue().keySet()) {
        for (Item k : diff.keySet()) {
            double predictedValue =
              diff.get(k).get(j).doubleValue() + e.getValue().get(j).doubleValue();
            double finalValue = predictedValue * freq.get(k).get(j).intValue();
            uPred.put(k, uPred.get(k) + finalValue);
            uFreq.put(k, uFreq.get(k) + freq.get(k).get(j).intValue());
        }
    }
    // ...
}

その後、以下のコードを使用して「クリーンな」予測を準備する必要があります。

HashMap<Item, Double> clean = new HashMap<Item, Double>();
for (Item j : uPred.keySet()) {
    if (uFreq.get(j) > 0) {
        clean.put(j, uPred.get(j).doubleValue() / uFreq.get(j).intValue());
    }
}
for (Item j : InputData.items) {
    if (e.getValue().containsKey(j)) {
        clean.put(j, e.getValue().get(j));
    } else if (!clean.containsKey(j)) {
        clean.put(j, -1.0);
    }
}

より大きなデータセットで検討する秘訣は、頻度の値が大きい(たとえば、> 1)アイテムエントリのみを使用することです。 予測が不可能な場合、その値は-1に等しくなることに注意してください。

最後に、非常に重要な注意事項です。アルゴリズムが正しく機能した場合、ユーザーが評価しなかったアイテムの予測だけでなく、ユーザーが評価したアイテムの繰り返しの評価も受け取る必要があります。 これらの繰り返される評価は変更しないでください。変更しないと、アルゴリズムの実装にバグがあることを意味します。

3.4. ヒント

SlopeOneアルゴリズムに影響を与える主な要因はいくつかあります。 精度と処理時間を向上させるためのヒントを次に示します。

  • 大規模なデータセットの場合、DB側でユーザーアイテムの評価を取得することを検討してください
  • 人々の関心は時間とともに変化する可能性があるため、評価を取得するための時間枠を設定します。これにより、入力データの処理に必要な時間も短縮されます。
  • 大きなデータセットを小さなデータセットに分割する–すべてのユーザーの予測を毎日計算する必要はありません。 ユーザーが予測されたアイテムを操作したかどうかを確認し、翌日の処理キューからユーザーを追加/削除できます。

4. 結論

このチュートリアルでは、SlopeOneアルゴリズムについて学ぶことができました。 さらに、アイテム推奨システムの協調フィルタリング問題を導入しました。

このチュートリアルの完全な実装は、GitHubプロジェクトにあります。