2

切换一堆 for 循环代码以使用并行流显然会导致代码的某些部分被忽略。

我正在使用带有 Java 11 的 MOA 和 Weka 来运行一个简单的推荐引擎示例,从 的源代码中获取线索,该示例moa.tasks.EvaluateOnlineRecomender使用 MOA 的内部任务设置来测试由提供的偏置正则化增量同时矩阵分解 (BRISM​​F) 实现的准确性恐鸟。我没有使用 MOA 准备好的MovielensDataset课程,而是切换到 Weka 的Instances,以寻找应用 Weka 的 ML 工具的前景。

处理大约一百万个实例(我使用的是 Movielens 1M 数据集)大约需要 13-14 分钟。为了看到改进,我想在并行流上运行它,当任务在大约 40 秒内完成时我开始怀疑。我发现它BRISMFPredictor.predictRating总是在并行流的主体内产生 0。以下是两种情况的代码:

初始化代码:

import com.github.javacliparser.FileOption;
import com.github.javacliparser.IntOption;

import moa.options.ClassOption;
import moa.recommender.predictor.BRISMFPredictor;
import moa.recommender.predictor.RatingPredictor;
import moa.recommender.rc.data.RecommenderData;
import weka.core.converters.CSVLoader;

...

private static ClassOption datasetOption;
private static ClassOption ratingPredictorOption;
private static IntOption sampleFrequencyOption;
private static FileOption defaultFileOption;

static {
    ratingPredictorOption = new ClassOption("ratingPredictor",
            's', "Rating Predictor to evaluate on.", RatingPredictor.class,
            "moa.recommender.predictor.BRISMFPredictor");
    sampleFrequencyOption = new IntOption("sampleFrequency",
            'f', "How many instances between samples of the learning performance.", 100, 0, 2147483647);
    defaultFileOption = new FileOption("file",
            'f', "File to load.",
            "C:\\Users\\shiva\\Documents\\Java-ML\\mlapp\\data\\ml-1m\\ratings.dat", "dat", false);
}

...和内部main()(Weka 的一个怪癖CSVLoader要求我将默认::分隔符替换为+

    var csvLoader = new CSVLoader();
    csvLoader.setSource(defaultFileOption.getFile());
    csvLoader.setFieldSeparator("+");
    var dataset = csvLoader.getDataSet();
    System.out.println(dataset.toSummaryString());

    var predictor = new BRISMFPredictor();
    predictor.prepareForUse();

    RecommenderData data = predictor.getData();
    data.clear();
    data.disableUpdates(false);

现在,在以下片段之间交替:

for (var instance : dataset) {
    var user = (int) instance.value(0);
    var item = (int) instance.value(1);
    var rating = instance.value(2);

    double predictedRating = predictor.predictRating(user, item);

    System.out.printf("User %d | Movie %d | Actual Rating %d | Predicted Rating %f%n",
                    user, item, Math.round(rating), predictedRating);
}

(现在是所有并发的菜鸟):

dataset.parallelStream().forEach(instance -> {
    var user = (int) instance.value(0);
    var item = (int) instance.value(1);
    var rating = instance.value(2);

    double predictedRating = predictor.predictRating(user, item);

    System.out.printf("User %d | Movie %d | Actual Rating %d | Predicted Rating %f%n",
                    user, item, Math.round(rating), predictedRating);
});

现在我决定,哎呀,也许这个操作不能并行完成,我把它切换到使用stream(). 即使这样,该段似乎也被完全忽略了,因为每次输出再次为 0.0

dataset.stream().forEach(instance -> {
    var user = (int) instance.value(0);
    var item = (int) instance.value(1);
    var rating = instance.value(2);

    double predictedRating = predictor.predictRating(user, item);

    System.out.printf("User %d | Movie %d | Actual Rating %d | Predicted Rating %f%n",
                    user, item, Math.round(rating), predictedRating);
});

我曾尝试从运行中删除打印语句,但无济于事。

显然,在第一种情况下,我在大约 13 分钟内得到了由实际评分和预测评分组成的预期输出行,但在第二种情况下发现预测评分为 0.0,执行时间非常短。有什么我错过的吗?

编辑: usingdataset.forEach()做同样的事情。也许是 lambdas 的一个怪癖?

4

0 回答 0