文章目录
背景
计划构建并优化一个覆盖前端与后端的个性化推荐系统中的离线召回模块。
此模块旨在通过高效的数据处理与分析,预先筛选出用户可能感兴趣的内容或商品,为后续的实时推荐流程提供丰富且精准的候选集。为了确保实施效果与性能,我们将设计最简前端界面以直观展示召回结果,同时构建后端服务来处理大量数据并响应前端请求。在前端部分,将开发用户基本的交互界面,允许用户在不影响系统性能的前提下,轻松浏览和反馈召回结果,支持切换用户查询,从而辅助验证推荐算法的有效性与准确性。
后端方面,以验证模型以及算法为主,验证过程中不采用离线计算框架(
Spark、Hadoop、Hive),不设置缓存层。具体参考
个性化推荐系统-离线召回模型框架
前端
使用vue开发
核心组件
<div class="title-with-margin">
<h2>个性化推荐系统</h2>
</div>
<!-- Top section with input and button -->
<div class="input-and-button">
<el-input v-model="uid" placeholder="请输入 UID(1-600)" clearable @keyup.enter="fetchUserData" />
<el-button type="primary" @click="fetchUserData(currentPage.value)">推荐</el-button>
<el-button type="primary" @click="gethistory(historycurrentPage.value)">历史行为</el-button>
</div>
<!-- Bottom section with pagination and list -->
<el-table :data="userData" style="width: 100%; margin-top: 1rem;">
<el-table-column prop="name" label="名称" width="220"> </el-table-column>
<el-table-column prop="mediatype" label="类型" width="150"> </el-table-column>
<el-table-column prop="catid" label="分类" width="100"> </el-table-column>
<el-table-column prop="abstract" label="简介" width="550" show-overflow-tooltip> </el-table-column>
<!-- <el-table-column prop="content" label="内容" > </el-table-column> -->
<el-table-column label="操作" width="150">
<template #default="{ row }">
<el-button size="small" type="text" @click="showreco(row)">操作</el-button>
</template>
</el-table-column>
</el-table>
模拟操作
<el-dialog v-model="showDialog" title="模拟操作" width="60%">
<div>
<el-row :gutter="20">
<el-col :span="6">
<!-- icon="el-icon-thumb" -->
<el-button :type="isLiked ? 'success' : 'primary'" type="text" @click="onLike(currentMedia)">点赞 </el-button>
</el-col>
<el-col :span="6">
<el-button :type="isPlay ? 'success' : 'primary'" type="text" @click="onView(currentMedia)">观看</el-button>
</el-col>
<el-col :span="6">
<el-button :type="isColl ? 'success' : 'primary'" @click="onCollect(currentMedia)">收藏</el-button>
</el-col>
<el-col :span="6">
<el-button :type="isShare ? 'success' : 'primary'" @click="onForward(currentMedia)">转发</el-button>
</el-col>
</el-row>
</div>
<div style="margin-top: 10px;">
<div >播放完整度</div>
<div class="slider-demo-block">
<el-slider v-model="playrate" show-input @change="playhandleChange"/>
</div>
</div>
<h3>相关推荐</h3>
<el-table :data="userData2" style="width: 100%; margin-top: 1rem;">
<el-table-column prop="name" label="名称" width="200"> </el-table-column>
<el-table-column prop="mediatype" label="类型" width="80"> </el-table-column>
<el-table-column prop="catid" label="分类" width="120"> </el-table-column>
<el-table-column prop="abstract" label="简介" width="300"> </el-table-column>
</el-table>
<el-pagination
background
layout="prev, pager, next"
:total="totalItems2"
:page-size="pageSize"
:current-page="currentPage2"
@current-change="handleCurrentChange2"
>
</el-pagination>
<template #footer>
<span class="dialog-footer">
<el-button @click="showDialog = false">关闭</el-button>
</span>
</template>
</el-dialog>
用户历史行为
<el-dialog v-model="historyshowDialog" title="用户历史操作记录" width="65%">
<el-table :data="historyuserData" style="width: 100%; margin-top: 1rem;">
<el-table-column prop="name" label="名称" width="200"> </el-table-column>
<el-table-column prop="mediatype" label="类型" width="80"> </el-table-column>
<el-table-column prop="catid" label="分类" width="100"> </el-table-column>
<el-table-column prop="abstract" label="简介" width="300"> </el-table-column>
<el-table-column prop="score" label="评分" width="80"> </el-table-column>
</el-table>
<el-pagination
background
layout="prev, pager, next"
:total="hisrorytotalItems"
:page-size="pageSize"
:current-page="historycurrentPage"
@current-change="historyhandleCurrentChange"
>
</el-pagination>
<template #footer>
<span class="dialog-footer">
<el-button @click="historyshowDialog = false">关闭</el-button>
</span>
</template>
</el-dialog>
const get_history_api = async (page = historycurrentPage.value) => {
console.log(historycurrentPage.value,page)
try {
const response = await axios.get('http://127.0.0.1:5000/useraction', {
params: {
page: parseInt(page),
pageSize: pageSize.value,
uid: uid.value
}
});
console.log(response.data.data)
historyuserData.value = response.data.data;
hisrorytotalItems.value = response.data.total;
} catch (error) {
console.error('Error fetching data:', error);
}
};
后端
导入依赖
from flask import Flask, request, jsonify
import pymysql
from flask_cors import CORS
启动服务
app = Flask(__name__)
CORS(app)
app.config['JSON_AS_ASCII'] = False
## db_config 数据库配置文件 参考之前的样例
def get_db_connection():
return pymysql.connect(**db_config)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000,debug=True)
根据uid获取推荐列表
解析参数,判断uid是否为0,分别进行不同处理
@app.route('/medialist', methods=['GET'])
def get_users():
page = int(request.args.get('page', 1))
per_page = int(request.args.get('pageSize', 10))
try:
uid = int(request.args.get('uid', '0'))
except ValueError:
uid = 0
print("uid",uid)
offset = (page - 1) * per_page
res = "0" if page > 0 else "-1"
total=300
connection = get_db_connection()
if(uid<=0):
try:
with connection.cursor() as cursor:
sql = "SELECT mediaid,name,CONCAT(catid,subcatid,tag) AS catid,mediatype,abstract FROM mediainfo ORDER BY RAND() LIMIT %s OFFSET %s"
#cursor.execute(sql, (per_page, offset))
#results = cursor.fetchall()
#users = [{'id': row[0], 'name': row[1]} for row in results]
cursor.execute(sql, (per_page, offset))
results = cursor.fetchall()
resdata = { "res": res,"total": total,"data":results}
return jsonify(resdata), 200
finally:
connection.close()
else:
total=100
try:
with connection.cursor() as cursor:
sql = "SELECT m.mediaid, m.name,CONCAT(m.catid,m.subcatid,m.tag) AS catid,m.mediatype,m.abstract FROM uid_reco ur JOIN mediainfo m ON ur.mediaid = m.mediaid WHERE ur.uid = %s LIMIT %s OFFSET %s"
#cursor.execute(sql, (per_page, offset))
#results = cursor.fetchall()
#users = [{'id': row[0], 'name': row[1]} for row in results]
cursor.execute(sql, (uid,per_page, offset))
results = cursor.fetchall()
resdata = { "res": res,"total": total,"data":results}
return jsonify(resdata), 200
finally:
connection.close()
相关推荐
内容相似度计算参考
相似度计算
@app.route('/relatereco', methods=['GET'])
def get_relate_reco():
page = int(request.args.get('page', 1))
per_page = int(request.args.get('pageSize', 10))
try:
mediaid = int(request.args.get('mediaid', '0'))
except ValueError:
mediaid = 0
print("relatereco - mediaid",mediaid)
offset = (page - 1) * per_page
res = "0" if page > 0 else "-1"
connection = get_db_connection()
total=100
try:
with connection.cursor() as cursor:
sql = "SELECT m.mediaid,m.name, CONCAT(m.catid, m.subcatid, m.tag) AS catid, m.mediatype, m.abstract FROM media_similarity ms JOIN mediainfo m ON ms.mediaid_2 = m.mediaid WHERE ms.mediaid_1 = %s LIMIT %s OFFSET %s"
cursor.execute(sql, (mediaid,per_page, offset))
results = cursor.fetchall()
resdata = { "res": res,"total": total,"data":results}
return jsonify(resdata), 200
finally:
connection.close()
用户历史记录
app.route('/useraction', methods=['GET'])
def get_user_action():
page = int(request.args.get('page', 1))
per_page = int(request.args.get('pageSize', 10))
try:
uid = int(request.args.get('uid', '0'))
except ValueError:
uid = 0
print("relatereco - mediaid",uid)
offset = (page - 1) * per_page
res = "0" if page > 0 else "-1"
total=100
connection = get_db_connection()
try:
with connection.cursor() as cursor:
sql = "SELECT m.mediaid,m.name,CONCAT(m.catid, m.subcatid, m.tag) AS catid, m.mediatype, m.abstract,u.score FROM user_rate u JOIN mediainfo m ON u.mediaid= m.mediaid WHERE u.uid = %s LIMIT %s OFFSET %s"
cursor.execute(sql, (uid,per_page, offset))
results = cursor.fetchall()
resdata = { "res": res,"total": total,"data":results}
return jsonify(resdata), 200
finally:
connection.close()
用户行为数据上报
def handle_user_action():
# 解析请求体中的 JSON 数据
data = request.get_json()
# 获取 uid, mediaid, score
uid = data.get('uid')
mediaid = data.get('mediaid')
score = data.get('score')
if not (uid and mediaid and score):
return jsonify({'error': 'Missing required fields'}), 400
connection = get_db_connection()
try:
with connection.cursor() as cursor:
sql = "insert user_rate (uid,mediaid,score)values(%s,%s,%s)"
cursor.execute(sql, (uid,mediaid, score))
results = cursor.fetchall()
resdata = { "res": results}
return jsonify(resdata), 200
finally:
connection.close()
return jsonify({'error': 'DB connection fields'}), 400
标签:mediaid,uid,get,离线,cursor,connection,召回,page,个性化
From: https://blog.csdn.net/zhangdonghuirjdd/article/details/141609347