使用 Mongoose 和 Node.js 构建协同过滤推荐系统

阅读时长 12 分钟读完

使用 Mongoose 和 Node.js 构建协同过滤推荐系统

推荐系统是电商和社交媒体中不可或缺的部分。它们使用各种技术来预测用户可能感兴趣的内容和产品,并向他们提供个性化推荐。协同过滤是一种常用的推荐算法,它基于用户之间的相似性和项目之间的相似性来产生推荐。在本文中,我们将使用 Mongoose 和 Node.js 来构建一个协同过滤推荐系统。

基础

在开始构建之前,我们需要了解协同过滤的基础知识。协同过滤可以分为两种类型:用户协同过滤和物品协同过滤。用户协同过滤基于相似用户之间的行为模式,并使用它们的行为来对用户进行推荐。物品协同过滤则基于相似的产品和服务,并使用其他用户的行为来对产品和服务进行推荐。

在这里,我们将使用用户协同过滤算法。该算法包括以下步骤:

  1. 找到与目标用户相似的其他用户。

  2. 找到这些用户喜欢的并且目标用户没有看过的物品。

  3. 将这些物品推荐给目标用户。

构建推荐系统

首先,我们需要创建一个 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();

}); }

然后,我们需要找到这些用户喜欢的但目标用户没有看过的物品。

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();

}

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

纠错
反馈