商品个性化推荐
任务要求:
二.原理讲解:
本项目的基于 用户的购买历史 和 商品之间的相似性 进行商品推荐。它主要包括两个核心部分:用户-商品矩阵 和 商品相似度计算。下面我会结合算法的原理和实际示例为你讲解这个推荐系统的工作原理。
2.1用户-商品矩阵(User-Product Matrix)
首先,推荐系统需要知道每个用户购买了哪些商品。在这段代码中,create_user_product_matrix 方法通过 pandas 的 pivot_table 方法创建了一个 用户-商品矩阵(User-Product Matrix),这个矩阵的每一行代表一个用户,每一列代表一个商品,值表示该用户是否购买了这个商品。
那么,通过 pivot_table 后,矩阵可能如下所示:
username |
101 |
102 |
103 |
---|---|---|---|
user1 |
1 |
1 |
0 |
user2 |
1 |
0 |
0 |
user3 |
0 |
1 |
1 |
- 1 表示该用户购买了对应商品,0 表示该用户未购买该商品。
2.2商品之间的相似度计算(Product Similarity)
为了给用户推荐相似商品,我们需要计算商品之间的相似度。这个系统通过 余弦相似度 来衡量商品之间的相似度。余弦相似度 是一种常用的相似度度量方法,计算的是两个向量之间夹角的余弦值。余弦值越大,表示两个商品的相似度越高。
其中:
商品根据上述的用户-商品矩阵可知商品 101 和 102 的被购买的记录向量分别是:
- 商品 101:[1, 1, 0] (商品 101被user1 和 user2 购买了)
- 商品 102:[1, 0, 1] (商品 102被user1, user3 购买了)
通过计算余弦相似度,可以得到它们的相似度分数。
在代码中,cosine_similarity(user_product_matrix.T) 计算的是 商品之间的相似度,并返回一个相似度矩阵,矩阵中的每个值表示两个商品的相似度。
示例:
101 |
102 |
103 | |
---|---|---|---|
101 |
1 |
0.5 |
0.2 |
102 |
0.5 |
1 |
0.3 |
103 |
0.2 |
0.3 |
1 |
- 例如,商品 101 和商品 102 之间的相似度是 0.5,表示这两个商品的购买模式有一定的重合。
2.3.推荐算法的核心步骤
a. 基于用户购买历史的推荐
当用户提供用户名时,系统会基于该用户的购买历史来推荐商品。具体步骤如下:
- 获取该用户购买的商品:从用户-商品矩阵中找到用户购买的商品。
- 计算相似商品的分数:对于用户已购买的每个商品,系统会找到与该商品相似的商品,并计算它们的相似度分数。如果用户尚未购买某个商品,就把它加到推荐列表中。
- 合并相似度分数:对于多个已购买商品的相似商品,系统会把它们的相似度分数累加起来,最终按总分数排序,选出前 top_n 个商品。
示例:
假设 user1 已经购买了商品 101 和 102,它们的相似商品如下:
- 请看如下相似度矩阵
goods_sn |
101 |
102 |
103 |
---|---|---|---|
101 |
1 |
0.5 |
0.3 |
102 |
0.5 |
1 |
0.2 |
- 商品 101 的相似商品是 102 和 103(假设它们的相似度为 0.5 和 0.3)。
- 商品 102 的相似商品是 101 和 103(假设它们的相似度为 0.5 和 0.2)。
系统会累加这些相似度分数,最终推荐 103 作为最优推荐商品,因为它的总相似度(0.3 + 0.2 = 0.5)最高。
b. 基于热门商品的推荐
如果没有提供用户名(即用户没有进行登录时),系统会推荐 购买最多的商品,也就是根据商品的购买次数来推荐最热门的商品。
示例:
假设以下是所有商品的购买次数:
- 商品 101:5 次
- 商品 102:3 次
- 商品 103:8 次
系统会推荐购买最多的 103、101、102。
2.4. 推荐结果
最终,get_top_n_recommendations 方法会返回前 top_n 个推荐商品。这个推荐可以是基于用户购买历史的个性化推荐,也可以是基于热门商品的通用推荐。
示例:
- 用户提供用户名:如果用户提供了用户名 user1,系统会根据其历史购买记录,推荐与其购买商品相似的商品。
- 没有提供用户名:系统会根据商品的购买次数来推荐最热门的商品。
三.开始实战演练
由于本人原先并未好好的研究过litemall的后端结果,如果在原先的项目中开始编写对于我说并不是很友好。同时在算法处理方面,我更为熟练的使用python进行相关方面的操作。接下来我将以python为主,告诉大家这个商品个性化的后端是怎么写的。
3.1数据准备及程序框架
购买记录数据,并根据数据创建一个购买记录表,其中属性包含用户账号以及商品Id或者编号。
程序框架:
recommendation_system/
│
├── appy.py # 主程序入口(相当于控制层)
├── data_loader.py # 数据获取相关的代码(从数据库中提取数据)
├── recommender.py # 商品推荐算法(计算商品相似度、生成推荐)
└── config.py # 配置文件(数据库连接配置)
3.2python 实现
详细代码请看如下链接,新增History.py程序是由于购买记录过少随机生成1000条购买记录方便实验!
33/商品推荐算法
四.前后端实现
哎这二个是个开源的项目对于他编写的前端与后端对于我来说还说还是太高级了。所以我并没有遵守该项目的相关编写规范。我是另用python编写的后端,前端直接修改某些内容,新增两个页面并且直接在页面中进行数据的请求。
4.1更改router中新增侧边菜单栏内容
即在router中的index.js这个部分中新增以下内容:
(这一部分是凭借我自学vue的相关内容从而做出来的但是还是不是很了解)
1 { 2 path: '/arithmetic', /*新增参数设置以及商品推荐模块*/ 3 component: Layout, 4 redirect: 'noredirect', 5 alwaysShow: true, 6 name: 'settingsManage', 7 meta: { 8 title: 'app.menu.arithmetic', 9 icon: 'chart' 10 }, 11 children: [ 12 { 13 path: 'parameters', 14 component: () => import('@/views/arithmetic/parameters.vue'), // 假设页面路径为 /views/settings/parameters.vue 15 name: 'parameters', 16 meta: { 17 title: 'app.menu.settings_parameters', 18 noCache: true 19 } 20 }, 21 { 22 path: 'recommendation', 23 component: () => import('@/views/arithmetic/recommendation.vue'), // 假设页面路径为 /views/recommendation/goods.vue 24 name: 'recommendation', 25 meta: { 26 title: 'app.menu.recommendation_goods', 27 noCache: true 28 } 29 } 30 ] 31 },
4.2参数设置页面
这个页面很简单但是,它的重点内容在如何根据输入的参数进行封装url并保存本地,方便商品推荐页面获取并发出请求。我的设想主要如下,根据我后端的编写,发送请求获得数据一共有以下4种情况:
无用户名 无推荐个数参数
http://localhost:5000/recommend
无用户名
http://127.0.0.1:5000/recommend?top_n=20
无推荐个数参数
http://127.0.0.1:5000/recommend?username=user1
两者都有:
http://127.0.0.1:5000/recommend?username=user2&top_n=5
<template> <div class="parameter-settings"> <el-card> <h3>参数设置</h3> <el-form :model="form" label-width="120px"> <!-- 推荐商品个数 --> <el-form-item label="推荐商品个数" prop="recommendationCount"> <el-input-number v-model="form.recommendationCount" :min="1" :max="100" label="推荐商品个数" /> </el-form-item><!-- 用户账号 --> <el-form-item label="用户名" prop="username"> <el-input v-model="form.username" placeholder="请输入用户名" :maxlength="20" /> </el-form-item><!-- 保存、重置 按钮 --> <el-form-item> <el-button type="primary" @click="saveSettings">保存</el-button> <el-button @click="resetSettings">重置</el-button> </el-form-item> </el-form> </el-card> </div> </template><script> import 'element-ui/lib/theme-chalk/index.css';export default { data() { return { form: { recommendationCount: undefined,// 默认推荐商品个数为空,用户可自行输入 username: ''// 默认没有用户账号 } }; }, mounted() { // 页面加载时,从 localStorage 中获取保存的参数和 URL const storedData = JSON.parse(localStorage.getItem('formData')); if (storedData) { this.form.username = storedData.username || ''; this.form.recommendationCount = storedData.top_n || ''; // 默认为 6 } }, methods: { saveSettings() { const recommendationCount = this.form.recommendationCount ; const username = this.form.username;let url = 'http://127.0.0.1:5000/recommend';if (username) { url += `?username=${username}`; }if (recommendationCount) { if (url.includes('?')) { url += `&top_n=${recommendationCount}`; } else { url += `?top_n=${recommendationCount}`; } }// 将生成的 URL 和表单数据存储到 localStorage const dataToSave = { url: url, username: username, top_n: recommendationCount }; localStorage.setItem('formData', JSON.stringify(dataToSave)); // 显示“保存成功”的消息提示 this.$message.success('保存成功'); }, resetSettings() { // 清空表单输入 this.form.recommendationCount = undefined; this.form.username = '';// 清除 localStorage 中的 URL 和表单数据 localStorage.removeItem('formData');// 通知父组件 URL 已被重置 this.$emit('urlGenerated', null); } } }; </script><style scoped> .parameter-settings { padding: 20px; }.el-card { padding: 20px; }.el-button { margin-top: 20px; } </style>
4.推荐商品页面
<template> <div class="container"> <div class="product-recommendations"> <el-card class="product-card" v-for="product in products" :key="product.id"> <!-- 商品图片 --> <img :src="product.pic_url" class="product-image" alt="商品图片" /><!-- 商品信息 --> <div class="product-details"> <h3>{{ product.name }}</h3> <p>商品详情: {{ product.brief }}</p> <p>零售价格: ¥{{ product.retail_price }}</p> <p>对比价格: ¥{{ product.counter_price }}</p> </div><!-- 按钮(你可以根据需要添加功能,如加入购物车等) --> <el-button type="primary" size="small" :disabled="true">立即购买</el-button> </el-card> </div> </div> </template><script> export default { data() { return { products: [] // 用来存储商品数据 }; }, mounted() { // 页面加载时,从 localStorage 中获取保存的参数和 URL const storedData = JSON.parse(localStorage.getItem('formData'));// 如果存在 URL 就使用它进行请求,否则使用默认 URL const url = storedData.url || 'http://127.0.0.1:5000/recommend';// 发送请求获取商品数据 this.fetchProductData(url); }, methods: { // 请求商品数据 async fetchProductData(url) { try { const response = await fetch(url); const data = await response.json();if (data.goods_info) { this.products = data.goods_info; } else { throw new Error("返回的商品数据格式不正确"); } } catch (error) { console.error("获取商品数据失败", error); // 你可以在这里显示提示信息,或者展示错误状态 } } } }</script><style scoped> .container{ display: flex; justify-content: center;} .product-recommendations { display: flex; flex-wrap: wrap; gap: 20px; /* 商品之间的间隔 */ justify-content: flex-start; /* 商品从左到右排列 */ padding: 50px; padding-left: 150px; }.product-card { width: 220px; /* 每个商品卡片的宽度 */ padding: 20px; box-sizing: border-box; text-align: center; border-radius: 10px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* 添加一些阴影效果 */ }.product-image { width: 80%; height: auto; margin-bottom: 10px; }.product-details { margin-bottom: 20px; }.el-button { width: 100%; } </style>
五.程序优化
5.1所面临的问题:
本项目面临的问题具体情况如下:
- 当新用户输入时无购买记录
- 当用户的购买记录十分少导致数据稀疏性该怎么办
对于问题1我觉得可以这样解决:无购买记录则推荐热门商品。
对于问题二我是这样想的:
在推荐列表里可以这样组成:
[相似度较高的商品]+[热门商品] (由于交作业时间紧急在这里没有做去重工作)
5.2着手解决
5.2.1对于问题1的相关解决方法
直接在位于核心推荐函数当中判断,根据用户-商品矩阵判断该用户是否存在,若不存在则没有购买记录直接推荐热门商品退出函数。
5.2.2对于问题2的相关解决方法
主要改动:
- 增加相似度阈值参数:在 get_top_n_recommendations 方法中增加了 threshold 参数,表示相似度阈值。
- 筛选低相似度商品:在计算相似商品时,检查 score >= threshold,只考虑那些相似度高于或等于阈值的商品。
示例解释:
假设你的阈值是 threshold = 0.5,那么:
- 如果用户购买了商品 A,商品 A 的相似商品 B 和 C 分别有相似度分数 0.7 和 0.4。
- 假设用户没有购买商品 B 和 C,那么:
- 商品 B(相似度 0.7)会被推荐,因为 0.7 >= 0.5。
- 商品 C(相似度 0.4)不会被推荐,因为 0.4 < 0.5
通过这种方式,可以有效地避免推荐相似度较低的商品,从而提升推荐质量和精准度。
经过阈值筛选等到有限个推荐商品数量,将该数量与输入的top_n的参数进行对比,若小于则取热门商品进行补充,这样就解决数据稀疏性的问题了。