【问题标题】:Mahout: adjusted cosine similarity for item based recommenderMahout:基于项目的推荐器的调整余弦相似度
【发布时间】:2015-06-07 18:40:40
【问题描述】:

对于一项任务,我应该测试不同类型的推荐器,我必须首先实施。我一直在四处寻找一个好的图书馆来做到这一点(我一开始就想到了 Weka)并偶然发现了 Mahout。

因此,我必须提出:a) 我对 Mahout 完全陌生 b) 我在推荐人及其算法方面没有很强的背景(否则我不会上这门课......)和 c)抱歉,我远不是世界上最好的开发者 ==> 如果你能使用外行术语(尽可能......),我将不胜感激:)

我一直在关注一些教程(例如this,以及part2),并在基于项目和基于用户的推荐器方面获得了一些初步结果。

但是,我对基于项目的预测不太满意。到目前为止,我只发现了没有考虑用户评分偏差的相似函数。我想知道是否有类似adjusted cosine similarity 的东西。有什么提示吗?

【问题讨论】:

  • 你可以尝试扩展 AbstractSimilarity 类并自己实现
  • 好的,谢谢,我会看看。我很惊讶我似乎找不到任何相关信息(其他人问同样的问题,即用型实现,...)
  • 我找到了这个类,现在已弃用。你知道是否有任何方法可以找到我应该使用的东西吗? (我正在使用 mahout-0.9,我认为它已被删除!)archive.cloudera.com/cdh5/cdh/5/mahout-0.8-cdh5.0.0/mahout-core/…

标签: java mahout recommendation-engine mahout-recommender cosine-similarity


【解决方案1】:

这是我创建的AdjustedCosineSimilarity 的示例。您必须记住,由于 sqrt 计算,这将比 PearsonCorrelationSimilarity 慢,但会产生更好的结果。至少对于我的数据集结果要好得多。但是您应该权衡一下质量/性能,并根据您的需要使用您想要的实现。

/**
 * Custom implementation of {@link AdjustedCosineSimilarity}
 * 
 * @author dmilchevski
 *
 */
public class AdjustedCosineSimilarity extends AbstractSimilarity {

  /**
   * Creates new {@link AdjustedCosineSimilarity}
   * 
   * @param dataModel
   * @throws TasteException
   */
    public AdjustedCosineSimilarity(DataModel dataModel)
            throws TasteException {
        this(dataModel, Weighting.UNWEIGHTED);
    }

    /**
     * Creates new {@link AdjustedCosineSimilarity}
     * 
     * @param dataModel
     * @param weighting
     * @throws TasteException
     */
    public AdjustedCosineSimilarity(DataModel dataModel, Weighting weighting)
            throws TasteException {
        super(dataModel, weighting, true);
        Preconditions.checkArgument(dataModel.hasPreferenceValues(),
                "DataModel doesn't have preference values");
    }

    /**
     * Compute the result
     */
    @Override
    double computeResult(int n, double sumXY, double sumX2, double sumY2, double sumXYdiff2) {
        if (n == 0) {
            return Double.NaN;
        }
        // Note that sum of X and sum of Y don't appear here since they are
        // assumed to be 0;
        // the data is assumed to be centered.
        double denominator = Math.sqrt(sumX2) * Math.sqrt(sumY2);
        if (denominator == 0.0) {
            // One or both parties has -all- the same ratings;
            // can't really say much similarity under this measure
            return Double.NaN;
        }
        return sumXY / denominator;
    }

    /**
     * Gets the average preference
     * @param prefs
     * @return
     */
    private double averagePreference(PreferenceArray prefs){
        double sum = 0.0;
        int n = prefs.length();
        for(int i=0; i<n; i++){
            sum+=prefs.getValue(i);
        }
        if(n>0){
            return sum/n;
        }
        return 0.0d;
    }

    /**
     * Compute the item similarity between two items
     */
    @Override
    public double itemSimilarity(long itemID1, long itemID2) throws TasteException {
        DataModel dataModel = getDataModel();
        PreferenceArray xPrefs = dataModel.getPreferencesForItem(itemID1);
        PreferenceArray yPrefs = dataModel.getPreferencesForItem(itemID2);
        int xLength = xPrefs.length();
        int yLength = yPrefs.length();

        if (xLength == 0 || yLength == 0) {
            return Double.NaN;
        }

        long xIndex = xPrefs.getUserID(0);
        long yIndex = yPrefs.getUserID(0);
        int xPrefIndex = 0;
        int yPrefIndex = 0;

        double sumX = 0.0;
        double sumX2 = 0.0;
        double sumY = 0.0;
        double sumY2 = 0.0;
        double sumXY = 0.0;
        double sumXYdiff2 = 0.0;
        int count = 0;

        // No, pref inferrers and transforms don't appy here. I think.

        while (true) {
            int compare = xIndex < yIndex ? -1 : xIndex > yIndex ? 1 : 0;
            if (compare == 0) {
                // Both users expressed a preference for the item
                double x = xPrefs.getValue(xPrefIndex);
                double y = yPrefs.getValue(yPrefIndex);
                long xUserId = xPrefs.getUserID(xPrefIndex);
                long yUserId = yPrefs.getUserID(yPrefIndex);

                double xMean = averagePreference(dataModel.getPreferencesFromUser(xUserId));
                double yMean = averagePreference(dataModel.getPreferencesFromUser(yUserId));

                sumXY += (x - xMean) * (y - yMean);
                sumX += x;
                sumX2 += (x - xMean) * (x - xMean);
                sumY += y;
                sumY2 += (y - yMean) * (y - yMean);
                double diff = x - y;
                sumXYdiff2 += diff * diff;
                count++;
            }
            if (compare <= 0) {
                if (++xPrefIndex == xLength) {
                    break;
                }
                xIndex = xPrefs.getUserID(xPrefIndex);
            }
            if (compare >= 0) {
                if (++yPrefIndex == yLength) {
                    break;
                }
                yIndex = yPrefs.getUserID(yPrefIndex);
            }
        }

        double result;

        // See comments above on these computations
        double n = (double) count;
        double meanX = sumX / n;
        double meanY = sumY / n;
        // double centeredSumXY = sumXY - meanY * sumX - meanX * sumY + n *
        // meanX * meanY;
        double centeredSumXY = sumXY - meanY * sumX;
        // double centeredSumX2 = sumX2 - 2.0 * meanX * sumX + n * meanX *
        // meanX;
        double centeredSumX2 = sumX2 - meanX * sumX;
        // double centeredSumY2 = sumY2 - 2.0 * meanY * sumY + n * meanY *
        // meanY;
        double centeredSumY2 = sumY2 - meanY * sumY;
//      result = computeResult(count, centeredSumXY, centeredSumX2,
//              centeredSumY2, sumXYdiff2);

        result = computeResult(count, sumXY, sumX2, sumY2, sumXYdiff2);

        if (!Double.isNaN(result)) {
            result = normalizeWeightResult(result, count,
                    dataModel.getNumUsers());
        }
        return result;
    }

}

【讨论】:

  • 非常感谢。我会在接下来的几天里尝试检查它!
猜你喜欢
  • 2019-09-08
  • 2018-01-23
  • 1970-01-01
  • 2011-11-23
  • 1970-01-01
  • 2017-04-04
  • 2018-08-03
  • 1970-01-01
  • 2014-12-02
相关资源
最近更新 更多