norbert.mynemo.core.selection.SvdRecommenderEvalFunction.java Source code

Java tutorial

Introduction

Here is the source code for norbert.mynemo.core.selection.SvdRecommenderEvalFunction.java

Source

/*
 * Copyright 2015 Norbert
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package norbert.mynemo.core.selection;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import norbert.mynemo.core.evaluation.PersonnalRecommenderEvaluator;
import norbert.mynemo.core.recommendation.RecommenderFamily;
import norbert.mynemo.core.recommendation.RecommenderType;
import norbert.mynemo.core.recommendation.configuration.SvdBasedRecommenderConfiguration;
import norbert.mynemo.core.recommendation.recommender.SvdBasedRecommender;

import org.apache.commons.math3.analysis.MultivariateFunction;
import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.eval.DataModelBuilder;
import org.apache.mahout.cf.taste.model.DataModel;

/**
 * This function renders optimizable the numbers of features and iterations for a SVD based
 * recommender. Indeed, the {@link #value(double[])} method takes the two numbers as parameter, and
 * returns the value of a metric produced by an evaluator for these numbers. A list of all
 * evaluations performed are kept, and can be accessed via the {@link #getEvaluations()} method.
 *
 * <p>
 * A minimum coverage is taken in account. Indeed, an evaluation with a coverage below the given
 * minimum coverage produces a worst value than an evaluation with an acceptable coverage.
 *
 * <p>
 * Because this class implements the {@link MultivariateFunction}, it can be used by a
 * {@link org.apache.commons.math3.optim.nonlinear.scalar.MultivariateOptimizer
 * MultivariateOptimizer}.
 */
class SvdRecommenderEvalFunction extends RecommenderEvalFunction implements MultivariateFunction {

    /**
     * Returns a unique <code>long</code> from the two given integers. The same integers will always
     * return the same value.
     */
    private static Long generateUniqueKey(int numFeatures, int numIterations) {
        return (((long) numFeatures) << 32) | numIterations;
    }

    private final Map<Long, Double> cache;
    private final DataModel dataModel;
    private final DataModelBuilder dataModelBuilder;
    private final double evaluationPercentage;
    private final List<RecommenderEvaluation> evaluations;
    private final PersonnalRecommenderEvaluator evaluator;
    private final boolean reuseIsAllowed;
    private final double trainingPercentage;
    private final RecommenderType type;

    /**
     * Creates a function for the given type of SVD based recommender.
     */
    public SvdRecommenderEvalFunction(SelectorConfiguration configuration, RecommenderType type,
            double minimumCoverage) {
        super(minimumCoverage);

        checkNotNull(configuration);
        checkArgument(type.getFamily() == RecommenderFamily.SVD_BASED);

        this.type = type;

        // extract the necessary data from the configuration
        dataModel = configuration.getDataModel();
        dataModelBuilder = configuration.getDataModelBuilder();
        evaluationPercentage = configuration.getEvaluationPercentage();
        evaluator = configuration.getEvaluator();
        reuseIsAllowed = configuration.reuseIsAllowed();
        trainingPercentage = configuration.getSpeed().getTrainingPercentage();

        evaluations = new ArrayList<>();
        cache = new HashMap<>();
    }

    /**
     * Returns all evaluations performed.
     */
    public Collection<RecommenderEvaluation> getEvaluations() {
        return evaluations;
    }

    @Override
    public double value(double[] point) {
        int numFeatures = (int) Math.round(point[0]);
        int numIterations = (int) Math.round(point[1]);
        Long cacheKey = generateUniqueKey(numFeatures, numIterations);

        // check the cache
        if (cache.containsKey(cacheKey)) {
            return cache.get(cacheKey);
        }

        // initialize the data for the evaluation
        SvdBasedRecommenderConfiguration configuration = new SvdBasedRecommenderConfiguration(type, numFeatures,
                numIterations, dataModel, reuseIsAllowed);
        SvdBasedRecommender recommenderBuilder = new SvdBasedRecommender(configuration);

        // run the evaluation
        double result = 0;
        try {
            result = evaluator.evaluate(recommenderBuilder, dataModelBuilder, dataModel, trainingPercentage,
                    evaluationPercentage);
        } catch (TasteException e) {
            throw new RuntimeException(e);
        }

        // save the evaluation and prepare the result
        evaluations.add(new RecommenderEvaluation(configuration, evaluator.getEvaluationReport()));

        double coverage = evaluator.getEvaluationReport().getCoverage();
        if (coverage < getMinimumCoverage()) {
            // if the minimum coverage is not reached, the return value depends on the coverage instead
            // of the number of neighbors
            result = valueFromCoverage(coverage);
        }

        cache.put(cacheKey, result);

        return result;
    }
}