所以我有这个问题。我正在使用节点js + mongoose,试图向用户推荐一部电影。背后的数学很简单,您可以查看其他用户对您的电影评分,找到与您相似的人,然后使用它来预测某部电影的评分。您为所有电影执行此操作,然后推荐具有最高预测评级的电影。在我的数据库中,我已经获得了100 k的评级,拥有1,6 k个电影和大约950个用户(从movielens获得了数据集)。我有一个评级模型,其中包含movieid,title,value和user。使用下面的代码,我可以预测一部电影,但是,当我尝试使用循环预测所有1部分,K6电影时,我得到这个消息“致命错误:CALL_AND_RETRY_LAST分配失败 - 处理内存不足”。我想我实施的方式占用了太多内存。这也需要很多时间。大约3-4秒预测一部电影,这意味着约100分钟预测所有1,6k部电影。有没有人有改进的建议?我对nodejs很新,这种异步环境让我感到不舒服。 继承我的代码:
Rating.find({title:"Batman Forever (1995)"}, function(err,rating){
if (err) throw err;
var tempSimilarValues = [];
var count = 0;
Rating.find({user: userV}, function(err,result) {
var tempMovieV;
var tempRatingArrayV = [];
var tempMovieArrayV = [];
while (result.length>0) {
tempMovieV=result.pop();
tempMovieArrayV.push(tempMovieV.movieid);
tempRatingArrayV.push(tempMovieV.value);
}
//calculating averageRatingV
var ratingV;
var avarageRatingV=0;
var countV=0;
for(ratingV in tempRatingArrayV){
countV++;
avarageRatingV=avarageRatingV+tempRatingArrayV[ratingV];
}
avarageRatingV=avarageRatingV/countV;
rating.forEach(function(obj){
Rating.find({user: obj.user}, function(err,result2) {
var tempRatingArray = [];
var tempMovieArray = [];
var tempMovie;
var tempAverage=0;
var ammount = result2.length
while (result2.length>0) {
tempMovie=result2.pop();
tempMovieArray.push(tempMovie.movieid);
tempAverage=tempAverage+tempMovie.value
tempRatingArray.push(tempMovie.value);
}
var averageU=tempAverage/ammount;
var similar = calSim(tempRatingArrayV,tempRatingArray, tempMovieArrayV, tempMovieArray);
if(obj.user!=userV){
tempSimilarValues.push([similar,obj.user,obj.value,averageU]);
}
count++;
if(count == rating.length){
tempSimilarValues.sort(function (a,b){
if (a[0] > b[0]) {
return -1;
}
if (a[0] < b[0]) {
return 1;
}
// a must be equal to b
return 0;
});
var similarValues=tempSimilarValues.slice(0,k);
var similarity;
var teller=0;
var nevner=0;
for (similarity in similarValues){
teller=teller+similarValues[similarity][0]*(similarValues[similarity][2]-similarValues[similarity][3]);
nevner = nevner + similarValues[similarity][0];
}
var pred = avarageRatingV + teller/nevner;
console.log(pred);
}
})
})
})
})
答案 0 :(得分:0)
使用回调来嵌套多种方法的代码会让您最终得到 Callback Hell 。处理场景的更好方法是使用 async 库,尤其是 series()
和 waterfall()
强>方法。
为了用类似的简短示例来说明这一点,该示例使用回调执行类似的多个操作来查找属于用户的评级(代码一),找到属于某个标题的一些评级(代码二),计算平均评级(代码三) )然后计算具有最高预测评级的那个(代码四):
codeOne(function(a){
codeTwo(function(b){
codeThree(function(c){
codeFour(function(d){
// Final callback code
})
})
})
})
应用运行任务函数数组的 series
方法,每个函数在前一个函数完成后运行。如果系列中的任何函数将错误传递给其回调,则不再运行任何函数,并立即使用错误值调用回调。否则,当任务完成时,回调会收到一系列结果。
async.series([
function(callback){
// code one
callback(null, 'one')
},
function(callback){
// code two
callback(null, 'two')
},
function(callback){
// code three
callback(null, 'three')
},
function(callback){
// code four
callback(null, 'four')
}],
// optional callback
function(err, results){
// results is ['one', 'two', 'three', 'four']
// final callback code
}
)
应用运行任务函数数组的 waterfall
方法,每个方法都将结果传递给数组中的下一个。但是,如果任何任务将错误传递给它们自己的回调,则不执行下一个函数,并立即调用主回调并显示错误。:
async.waterfall([
function(callback){
// code one
callback(null, 'one', 'two')
},
function(arg1, arg2, callback){
// arg1 is equals 'one' and arg2 is 'two'
// code three
callback(null, 'three')
},
function(arg1, callback){
// arg1 is 'three'
// code four
callback(null, 'four');
}], function (err, result) {
// result is 'four'
}
)
在您的示例代码中,建议重新构建它以使用瀑布方法。这是我在使用异步库实现逻辑的过程,而不是100%肯定你想要实现的目标,所以这可能不起作用,但你可能会得到这个概念:
async.waterfall([
function(callback){
// code one - calculate the average rating for user
Rating.find({user: userV}, function(err,result) {
if (err) callback(err);
var ratingV,
avarageRatingV = 0,
countV = 0,
tempRatingArray = result.map(function(rating){
return rating.value;
}),
tempMovieArrayV = result.map(function(rating){
return rating.movieid;
});
//calculating averageRatingV
for(ratingV in tempRatingArrayV){
countV++;
avarageRatingV = avarageRatingV + tempRatingArrayV[ratingV];
}
avarageRatingV=avarageRatingV/countV;
callback(null, avarageRatingV, tempRatingArrayV, tempMovieArrayV);
})
},
function(arg1, arg2, arg3, callback){
// arg1 equals averageRatingV
// arg2 equals tempRatingArrayV
// arg3 equals tempMovieArrayV
// code to return the users for "Batman Forever (1995)" movie ratings
Rating.find({ title: "Batman Forever (1995)" }, function(err, ratings){
if (err) callback(err);
var users = ratings.map(function(r){ return r.user; });
callback(null, arg1, arg2, arg3, users);
});
},
function(arg1, arg2, arg3, arg4, callback){
// arg1 is averageRatingV,
// arg2 equals tempRatingArrayV,
// arg3 equals tempMovieArrayVis ratings
// arg4 is the users array for "Batman Forever (1995)" movie ratings
// code to return
Rating.find({"user": { "$in": arg4 }}, function(err, result) {
// do the necessary calculations here
callback(null, result);
});
}], function (err, result) {
// final result
}
)