使用 Mongoose 和 Node.js 构建协同过滤推荐系统
推荐系统是电商和社交媒体中不可或缺的部分。它们使用各种技术来预测用户可能感兴趣的内容和产品,并向他们提供个性化推荐。协同过滤是一种常用的推荐算法,它基于用户之间的相似性和项目之间的相似性来产生推荐。在本文中,我们将使用 Mongoose 和 Node.js 来构建一个协同过滤推荐系统。
基础
在开始构建之前,我们需要了解协同过滤的基础知识。协同过滤可以分为两种类型:用户协同过滤和物品协同过滤。用户协同过滤基于相似用户之间的行为模式,并使用它们的行为来对用户进行推荐。物品协同过滤则基于相似的产品和服务,并使用其他用户的行为来对产品和服务进行推荐。
在这里,我们将使用用户协同过滤算法。该算法包括以下步骤:
找到与目标用户相似的其他用户。
找到这些用户喜欢的并且目标用户没有看过的物品。
将这些物品推荐给目标用户。
构建推荐系统
首先,我们需要创建一个 MongoDB 数据库,并使用 Mongoose 模块来连接它。在这里,我们创建了一个名为 recommendation 的数据库。
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/recommendation', { useNewUrlParser: true }); const db = mongoose.connection; db.on('error', console.error.bind(console, 'MongoDB connection error:'));
接下来,我们需要一个数据库模式来存储用户、项目和它们之间的评级。用户和项目将存储在各自的集合中,并使用用户 ID 和项目 ID 进行索引。评级将存储在评级集合中,并使用用户 ID 和项目 ID 进行索引。
const userSchema = new mongoose.Schema({ userId: { type: String, unique: true }, name: String, }); const User = mongoose.model('User', userSchema);
const itemSchema = new mongoose.Schema({ itemId: { type: String, unique: true }, name: String, }); const Item = mongoose.model('Item', itemSchema);
const ratingSchema = new mongoose.Schema({ userId: { type: String, index: true }, itemId: { type: String, index: true }, rating: Number, }); const Rating = mongoose.model('Rating', ratingSchema);
然后,我们需要编写代码来加载数据。在这里,我们为用户、项目和评级创建了三个 CSV 文件(user.csv、item.csv 和 rating.csv)。我们使用 Node.js 的 csv-parse 模块来解析这些文件,并将它们存储到 MongoDB 数据库中。
const fs = require('fs'); const parse = require('csv-parse'); const async = require('async');
function loadUser() { return new Promise((resolve, reject) => { const parser = parse({ columns: true, delimiter: ',' }); const input = fs.createReadStream('user.csv'); const users = [];
-- -------------------- ---- ------- --------------------- -- -- - --- ------- ----- ------- - -------------- - ------------------- - --- ---------------- ----- -- -- - --- ---- - - -- - - ------------- ---- - ----- ---- - --- ------ ------- ---------------- ----- ------------- --- ----- ------------ - ---------- --- ------------------ ----- -- - ------------ --- -------------------
}); }
function loadItem() { return new Promise((resolve, reject) => { const parser = parse({ columns: true, delimiter: ',' }); const input = fs.createReadStream('item.csv'); const items = [];
-- -------------------- ---- ------- --------------------- -- -- - --- ------- ----- ------- - -------------- - ------------------- - --- ---------------- ----- -- -- - --- ---- - - -- - - ------------- ---- - ----- ---- - --- ------ ------- ---------------- ----- ------------- --- ----- ------------ - ---------- --- ------------------ ----- -- - ------------ --- -------------------
}); }
function loadRating() { return new Promise((resolve, reject) => { const parser = parse({ columns: true, delimiter: ',' }); const input = fs.createReadStream('rating.csv'); const ratings = [];
-- -------------------- ---- ------- --------------------- -- -- - --- ------- ----- ------- - -------------- - --------------------- - --- ---------------- ----- -- -- - ----- ------------------- ----- -------- -- - ----- ---- - ----- -------------- ------- ------------- ---------- ----- ---- - ----- -------------- ------- ------------- ---------- ----- --------- - --- -------- ------- ------------ ------- ------------ ------- ------------- --- ----- ----------------- --- ---------- --- ------------------ ----- -- - ------------ --- -------------------
}); }
async function loadData() { await loadUser(); await loadItem(); await loadRating(); }
loadData();
现在,我们已经将用户、项和评级加载到数据库中。接下来,我们需要实现协同过滤算法。
首先,我们需要找到与目标用户相似的其他用户。在这里,我们使用了皮尔逊相关系数来衡量用户之间的相似性。
function pearsonCorrelation(user1, user2) { const user1Ratings = user1.ratings; const user2Ratings = user2.ratings;
const user1Sum = user1Ratings.reduce((sum, rating) => sum + rating.rating, 0); const user2Sum = user2Ratings.reduce((sum, rating) => sum + rating.rating, 0);
const user1Mean = user1Sum / user1Ratings.length; const user2Mean = user2Sum / user2Ratings.length;
let numerator = 0; let denominator1 = 0; let denominator2 = 0;
for (let i = 0; i < user1Ratings.length; i++) { const rating1 = user1Ratings[i]; const rating2 = user2Ratings.find(r => r.itemId === rating1.itemId); if (rating1 && rating2) { const deviation1 = rating1.rating - user1Mean; const deviation2 = rating2.rating - user2Mean; numerator += deviation1 * deviation2; denominator1 += deviation1 * deviation1; denominator2 += deviation2 * deviation2; } }
const denominator = Math.sqrt(denominator1) * Math.sqrt(denominator2); return denominator ? numerator / denominator : 0; }
function findSimilarUsers(userId) { return new Promise(async (resolve, reject) => { const user = await User.findOne({ userId: userId }).populate('ratings').exec(); const otherUsers = await User.find({ userId: { $ne: userId } }).populate('ratings').exec();
const similarities = otherUsers.map(user2 => { const similarity = pearsonCorrelation(user, user2); return { userId: user2.userId, similarity: similarity }; }); similarities.sort((a, b) => b.similarity - a.similarity); resolve(similarities);
}); }
然后,我们需要找到这些用户喜欢的但目标用户没有看过的物品。
async function findRecommendedItems(userId) { const user = await User.findOne({ userId: userId }).populate('ratings').exec(); const similarUsers = await findSimilarUsers(userId);
const recommendations = [];
for (let i = 0; i < similarUsers.length; i++) { const similarUser = similarUsers[i]; const ratings = await Rating.find({ userId: similarUser.userId }).exec();
for (let j = 0; j < ratings.length; j++) { const rating = ratings[j]; const existingRating = user.ratings.find(r => r.itemId === rating.itemId); if (!existingRating) { const item = await Item.findOne({ itemId: rating.itemId }).exec(); recommendations.push({ itemId: item.itemId, name: item.name, rating: rating.rating, similarity: similarUser.similarity }); } }
}
recommendations.sort((a, b) => b.similarity - a.similarity); return recommendations; }
最后,我们可以使用 Express.js 来创建一个 RESTful API,并将推荐作为 JSON 对象返回。
const express = require('express'); const app = express();
app.get('/users/:userId/recommendations', async (req, res) => { const userId = req.params.userId; const recommendations = await findRecommendedItems(userId); res.json(recommendations); });
app.listen(3000, () => { console.log('Example app listening on port 3000!'); });
结论
使用 Mongoose 和 Node.js 构建协同过滤推荐系统是一个有意义的项目。这样的推荐系统可以个性化地为用户推荐产品和服务,并在电商和社交媒体中提供更好的用户体验。在这篇文章中,我们介绍了协同过滤的基础知识,并提供了详细的代码示例。这些示例可以作为入门教程,帮助开发人员构建自己的推荐系统。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67720b886d66e0f9aad3fbbe