切换一堆 for 循环代码以使用并行流显然会导致代码的某些部分被忽略。
我正在使用带有 Java 11 的 MOA 和 Weka 来运行一个简单的推荐引擎示例,从 的源代码中获取线索,该示例moa.tasks.EvaluateOnlineRecomender
使用 MOA 的内部任务设置来测试由提供的偏置正则化增量同时矩阵分解 (BRISMF) 实现的准确性恐鸟。我没有使用 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 的一个怪癖?