使用Java Stream代替for循环会跳过代码执行

时间:2018-12-23 07:06:56

标签: java java-stream weka moa

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

我正在将MOA和Weka与Java 11一起运行一个简单的推荐引擎示例,并从moa.tasks.EvaluateOnlineRecomender的源代码中获取线索,该示例使用MOA的内部任务设置来测试有偏正则化增量式同时进行的准确性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,并且执行时间很短。有什么我想念的吗?

编辑:使用dataset.forEach()做同样的事情。也许是古怪的lambdas?

0 个答案:

没有答案