基于协同过滤推荐的在线课程选修系统

基于协同过滤推荐的在线课程选修系统

demo

网站查看 http://course.qsxbc.com/all_course/
点我查看
效果
在这里插入图片描述

功能

登录注册、点赞收藏、评分评论,课程推荐,热门课程,个人中心,可视化,后台管理,课程选修

推荐算法

# -*-coding:utf-8-*-
"""
@contact: 微信 1257309054
@file: recommend_user.py
@time: 2024/6/8 16:21
@author: LDC
使用Keras框架实现一个深度学习推荐算法
"""

import collections
import math
import os
import django
import operator
import numpy as np
from course.models import *
from k_means_utils import predict

os.environ["DJANGO_SETTINGS_MODULE"] = "course_manager.settings"
django.setup()


def get_default_recommend(user_id):
    # 获取默认推荐
    # 获取用户注册时选择的类别
    category_ids = []
    us = UserSelectTypes.objects.get(user_id=user_id)
    for category in us.category.all():
        category_ids.append(category.id)

    course_list = CourseInfo.objects.filter(tags__in=category_ids).distinct().order_by("-collect_num")[:30]
    return course_list


class UserCf:
    # 基于用户协同算法来获取推荐列表
    """
    利用用户的群体行为来计算用户的相关性。
    计算用户相关性的时候我们就是通过对比他们选修过多少相同的课程相关度来计算的
    举例:
    --------+--------+--------+--------+--------+
            |   X    |    Y   |    Z   |    R   |
    --------+--------+--------+--------+--------+
        a   |   1    |    1   |    1   |    0   |
    --------+--------+--------+--------+--------+
        b   |   1    |    0   |    1   |    0   |
    --------+--------+--------+--------+--------+
        c   |   1    |    1   |    0   |    1   |
    --------+--------+--------+--------+--------+

    a用户选修了:X、Y、Z
    b用户选修了:X、Z
    c用户选修了:X、Y、R

    那么很容易看到a用户和b、c用户非常相似,给a用户推荐课程R,
    给b用户推荐课程Y
    给c用户推荐课程Z
    这就是基于用户的协同过滤。
    a用户向量为(1,1,1,0)
    b用户向量为(1,0,1,0)
    c用户向量为(1,1,0,1)
    找a用户的相似用户,则计算a向量与其他向量的夹角即可,夹角越小则说明越相近
    利用求高维空间向量的夹角,可以估计两组数据的吻合程度
    """

    # 获得初始化数据
    def __init__(self, data):
        self.data = data

    # 计算N维向量的夹角
    def calc_vector_cos(self, a, b):
        '''
        cos=(ab的内积)/(|a||b|)
        :param a: 向量a
        :param b: 向量b
        :return: 夹角值
        '''
        a_n = np.array(a)
        b_n = np.array(b)
        if any(b_n) == 0:
            return 0
        cos_ab = a_n.dot(b_n) / (np.linalg.norm(a_n) * np.linalg.norm(b_n))
        print('值为', cos_ab)
        return round(cos_ab, 2)

    # 计算与当前用户的距离,获得最临近的用户
    def nearest_user(self, username, n=2):
        distances = {}
        # 用户,相似度
        # 遍历整个数据集
        for user, rate_set in self.data.items():
            # 非当前的用户
            if user != username:
                print('获取{}与{}的向量夹角'.format(username, user))
                vector_a = tuple(self.data[username].values())
                vector_b = tuple(self.data[user].values())
                distance = self.calc_vector_cos(vector_a, vector_b)
                # 计算两个用户的相似度
                distances[user] = distance
        # 排序,按向量夹角由小到到排序
        closest_distance = sorted(distances.items(), key=operator.itemgetter(1), reverse=True)
        # 最相似的N个用户
        closest_users = []
        for cd in closest_distance:
            if cd[1] == 1:
                closest_users.append(cd)
            else:
                if len(closest_users) >= n:
                    break
                closest_users.append(cd)
        print("closest user:", closest_users)
        return closest_users

    # 给用户推荐课程
    def recommend(self, username, n=1):
        recommend = set()
        nearest_user = self.nearest_user(username, n)  # 获取最相近的n个用户
        for user_id, _ in nearest_user:
            for usercourse in UserCourse.objects.filter(user_id=user_id):
                if usercourse.course.id not in self.data[username].keys():
                    recommend.add(usercourse.course.id)
        return recommend


# 用户推荐
def recommend_by_user_id(user_id, is_mix=False):
    # 通过用户协同算法来进行推荐
    current_user = User.objects.get(id=user_id)
    # 如果当前用户没有选修过课程,则按照收藏量降序返回
    if current_user.usercourse_set.count() == 0:
        if is_mix:
            return []
        return get_default_recommend(user_id)
    data = {}
    course_ids = []
    other_user_ids = set()
    # 把该用户选修过的课程变成向量字典:{'用户id': {'课程1id': 1, '课程2id': 1...}}
    for u_course in current_user.usercourse_set.all():
        # 遍历用户选修过的课程
        if not data:
            data[current_user.id] = {u_course.course.id: 1}  # 已选课程,设置值为1
        else:
            data[current_user.id][u_course.course.id] = 1
        course_ids.append(u_course.course)
        # 获取其他选修过该课程的用户id
        for usercourse in UserCourse.objects.filter(course=u_course.course):
            if usercourse.user.id != current_user.id:
                other_user_ids.add(usercourse.user.id)

    # 把选修过其中课程的用户选修过的课程变成向量字典:{'用户2id': {'课程1id': 0, '课程2id': 1...}}
    for other_user in User.objects.filter(pk__in=other_user_ids):
        other_user_id = other_user.id
        for i in range(len(course_ids)):
            course = course_ids[i]
            if UserCourse.objects.filter(user_id=other_user_id, course=course):
                is_select = 1
            else:
                is_select = 0
            if other_user_id not in data:
                data[other_user_id] = {course.id: is_select}  # 已选课程,设置值为1,未选课程设置为0
            else:
                data[other_user_id][course.id] = is_select

    user_cf = UserCf(data=data)
    recommend_ids = user_cf.recommend(current_user.id, 1)

    if not recommend_ids:
        # 如果没有找到相似用户则按照收藏量降序返回
        if is_mix:
            return []
        return get_default_recommend(user_id)
    if is_mix:
        return list(recommend_ids)
    return CourseInfo.objects.filter(is_show=True, id__in=recommend_ids).order_by('-select_num')


# 物品推荐
class ItemCf:
    # 基于物品协同算法来获取推荐列表
    '''
    1.构建⽤户–>物品的对应表
    2.构建物品与物品的关系矩阵(同现矩阵)
    3.通过求余弦向量夹角计算物品之间的相似度,即计算相似矩阵
    4.根据⽤户的历史记录,给⽤户推荐物品
    '''

    def __init__(self, user_id):
        self.user_id = user_id  # 用户id

    def get_data(self):
        # 获取用户评分过的课程
        rate_courses = RateCourse.objects.filter()
        if not rate_courses:
            return False
        datas = {}
        for rate_course in rate_courses:
            user_id = rate_course.user_id
            if user_id not in datas:
                datas.setdefault(user_id, {})
                datas[user_id][rate_course.course.id] = rate_course.mark
            else:
                datas[user_id][rate_course.course.id] = rate_course.mark

        return datas

    def similarity(self, data):
        # 1 构造物品:物品的共现矩阵
        N = {}  # 喜欢物品i的总⼈数
        C = {}  # 喜欢物品i也喜欢物品j的⼈数
        for user, item in data.items():
            for i, score in item.items():
                N.setdefault(i, 0)
                N[i] += 1
                C.setdefault(i, {})
                for j, scores in item.items():
                    if j != i:
                        C[i].setdefault(j, 0)
                        C[i][j] += 1
        print("---1.构造的共现矩阵---")
        print('N:', N)
        print('C', C)
        # 2 计算物品与物品的相似矩阵
        W = {}
        for i, item in C.items():
            W.setdefault(i, {})
            for j, item2 in item.items():
                W[i].setdefault(j, 0)
                W[i][j] = C[i][j] / math.sqrt(N[i] * N[j])
        print("---2.构造的相似矩阵---")
        print(W)
        return W

    def recommand_list(self, data, W, user, k=3, N=10):
        '''
        # 3.根据⽤户的历史记录,给⽤户推荐物品
        :param data: 用户数据
        :param W: 相似矩阵
        :param user: 推荐的用户
        :param k: 相似的k个物品
        :param N: 推荐物品数量
        :return:
        '''

        rank = {}
        for i, score in data[user].items():  # 获得⽤户user历史记录,如A⽤户的历史记录为{'唐伯虎点秋香': 5, '逃学威龙1': 1, '追龙': 2}
            for j, w in sorted(W[i].items(), key=operator.itemgetter(1), reverse=True)[0:k]:  # 获得与物品i相似的k个物品
                if j not in data[user].keys():  # 该相似的物品不在⽤户user的记录⾥
                    rank.setdefault(j, 0)
                    rank[j] += float(score) * w  # 预测兴趣度=评分*相似度
        print("---3.推荐----")
        print(sorted(rank.items(), key=operator.itemgetter(1), reverse=True)[0:N])
        return sorted(rank.items(), key=operator.itemgetter(1), reverse=True)[0:N]

    def recommendation(self, k=3, N=10):
        """
        给用户推荐相似课程
        :param user: 推荐的用户
        :param k: 相似的k个物品
        :param N: 推荐物品数量
        """
        data = self.get_data()
        if not data or self.user_id not in data:
            # 用户没有评分过任何课程,就返回空列表
            return []

        W = self.similarity(data)  # 计算物品相似矩阵
        sort_rank = self.recommand_list(data, W, self.user_id, k, N)  # 推荐
        return sort_rank


def recommend_by_item_id(user_id, is_mix=False):
    # 物品推荐
    cf_list = ItemCf(user_id).recommendation()  # 物品协同过滤得到的推荐列表
    course_ids = [s[0] for s in cf_list]
    if is_mix:
        return course_ids
    course_list = CourseInfo.objects.filter(id__in=course_ids).distinct().order_by("-select_num")
    if not course_list:
        # 推荐列表为空
        if is_mix:
            return []
        return get_default_recommend(user_id)
    return course_list


# k-means推荐
def recommend_by_k_mean(user_id, course_id=None, is_mix=False):
    # 使用机器学习K-means聚类算法推荐用户喜欢的课程
    try:
        data = []  # 用户课程类型挑选列表
        tag_dict = collections.OrderedDict()  # 课程类型字典(有序字典)

        # 获取所有类型,并设置值为0
        for tag in Tags.objects.filter():
            tag_dict[tag.name] = 0

        # 获取用户喜欢的课程类型
        us = UserSelectTypes.objects.get(user_id=user_id)
        for category in us.category.filter():
            # 在类型字典中设置用户喜欢的类型为1
            tag_dict[category.name] = 1
        data.append(list(tag_dict.values()))
        tag_like_list = predict(data)  # 预测数据
        if not tag_like_list:
            # 预测推荐集合为空,则返回用户注册时选择的类别
            if is_mix:
                return []
            return get_default_recommend(user_id)
        index = 0
        recommend_tag = []
        for tag, value in tag_dict.items():
            if tag_like_list[index] == 1:
                # 用户喜欢的课程类型
                recommend_tag.append(tag)
            index += 1
        print('推荐的类型', recommend_tag)
        rank_set = set()  # 推荐课程id集合
        # 获取各推荐课程类型中排行前三的课程推荐给用户,其中排行按照收藏量来计算
        for tag in recommend_tag:
            courses = CourseInfo.objects.filter(tags__name=tag).order_by("-collect_num")[:5]
            for course in courses:
                rank_set.add(course.id)
        print('推荐的列表id', rank_set)
        if is_mix:
            return list(rank_set)
        if rank_set:
            course_list = CourseInfo.objects.filter(id__in=rank_set).exclude(id=course_id).distinct().order_by(
                "-collect_num")
            return course_list
    except Exception as e:
        print('k-means出错', e)
    # 预测推荐集合为空,则返回用户注册时选择的类别
    if is_mix:
        return []
    return get_default_recommend(user_id)


# 混合推荐
def recommend_by_mix(user_id):
    recommend_user_ids = recommend_by_user_id(user_id, is_mix=True)  # 基于用户推荐
    print('recommend_user_ids', recommend_user_ids)
    recommend_item_ids = recommend_by_item_id(user_id, is_mix=True)  # 基于物品推荐
    print('recommend_item_ids', recommend_item_ids)
    recommend_kmean_ids = recommend_by_k_mean(user_id, is_mix=True)  # 基于k-means推荐
    print('recommend_kmean_ids', recommend_kmean_ids)
    recommend_ids = list(set(recommend_user_ids + recommend_item_ids + recommend_kmean_ids)) # 总的推荐列表
    print('总的推荐列表', recommend_ids)
    course_list = CourseInfo.objects.filter(is_show=True, id__in=recommend_ids).order_by('-select_num')
    if not course_list:
        # 推荐列表为空
        return get_default_recommend(user_id)
    return course_list

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/691437.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

商城项目【尚品汇】07分布式锁-2 Redisson篇

文章目录 1 Redisson功能介绍2 Redisson在Springboot中快速入门(代码)2.1 导入依赖2.2 Redisson配置2.3 将自定义锁setnx换成Redisson实现(可重入锁) 3 可重入锁原理3.1 自定义分布式锁setnx为什么不可以重入3.2 redisson为什么可…

opera打不开网页最简单的解决办法

如果以上为解决问题,继续下面操作 检查网络连接: 确认您的电脑已连接到互联网。 检查网络连接是否稳定,网络速度慢或链路拥堵可能会导致网页加载失败。 修改Local State文件: 关闭Opera浏览器。 定位到Opera浏览器的配置…

RPA影刀 | 设置当前时间

1. 新建流程 2. 创建指令 指令1:获取当前日期时间 指令3:日期时间转文本 %Y:年 %m:月 %d:日期 其他字符自由添加。 常用格式如下: %Y年%m月%d日%Y-%m-%d%Y_%m_%d%Y%m%d 3. 运行流程

STM32F103C8T6基于HAL库完成uC/OS-III多任务程序

一、在STM32CubeMX中建立工程 配置RCC 配置SYS 配置PC13为GPIO_Output 配置USART1 生成代码 二、获取uC/OS-III源码 官网下载地址:Micrium Software and Documentation - Silicon Labs 网盘下载:百度网盘 请输入提取码 提取码:lzjl 三、复…

HIP的应用可移植性

Application portability with HIP — ROCm Blogs (amd.com) 许多科学应用程序在配备AMD的计算平台和超级计算机上运行,包括Frontier,这是世界上第一台Exascale系统。这些来自不同科学领域的应用程序通过使用Heterogeneous-compute Interface for Portab…

AI论文速读 | 2024[KDD]GinAR—变量缺失端到端多元时序预测

题目:GinAR: An End-To-End Multivariate Time Series Forecasting Model Suitable for Variable Missing 作者:Chengqing Yu(余澄庆), Fei Wang(王飞), Zezhi Shao(邵泽志), Tangw…

实战 | 通过微调SegFormer改进车道检测效果(数据集 + 源码)

背景介绍 SegFormer:实例分割在自动驾驶汽车技术的快速发展中发挥了关键作用。对于任何在道路上行驶的车辆来说,车道检测都是必不可少的。车道是道路上的标记,有助于区分道路上可行驶区域和不可行驶区域。车道检测算法有很多种,每…

【python报错】TypeError: ‘dict_values‘ Object IsNot Subscriptable

【Python报错】TypeError: ‘dict_values’ object is not subscriptable 在Python中,字典(dict)提供了几种不同的视图对象,包括dict_keys、dict_values和dict_items。这些视图对象允许你以只读方式遍历字典的键、值或键值对。如果…

【大学物理】期末复习双语笔记

3 vectors and scalar 20 damped harmonic motion,forced harmonic motion, superposition of SHM damped harmonic motion underdamped motion:欠阻尼 critical damped零界阻尼 over damped过阻尼 energy of damped harmonic motion application of damped oscillation:减震器…

springboot + Vue前后端项目(第十五记)

项目实战第十五记 写在前面1.后端接口实现1.1 用户表添加角色字段1.2 角色表增加唯一标识字段1.3 UserDTO1.4 UserServiceImpl1.5 MenuServiceImpl 2. 前端实现2.1 User.vue2.2 动态菜单设计2.2.1 Login.vue2.2.2 Aside.vue 2.3 动态路由设计2.3.1 菜单表新增字段page_path2.3.…

HuggingFace团队亲授大模型量化基础: Quantization Fundamentals with Hugging Face

Quantization Fundamentals with Hugging Face 本文是学习https://www.deeplearning.ai/short-courses/quantization-fundamentals-with-hugging-face/ 这门课的学习笔记。 What you’ll learn in this course Generative AI models, like large language models, often exce…

基于OpenVINO实现无监督异常检测

异常检测(AD) 在欺诈检测、网络安全和医疗诊断等关键任务应用中至关重要。由于数据的高维性和底层模式的复杂性,图像、视频和卫星图像等视觉数据中的异常检测尤其具有挑战性。然而,视觉异常检测对于检测制造中的缺陷、识别监控录像中的可疑活动以及检测医…

应用广义线性模型二|二响应广义线性模型

系列文章目录 文章目录 系列文章目录一、二响应模型的不同表达方式和响应函数二、二响应模型的性质(一)二响应变量的条件数学期望与方差(二)二响应模型参数的极大似然估计(三)二响应模型的优势 三、二响应模…

算法人生(21):从“React框架”看“情绪管理”

说起React框架,我们知道它是一种由Facebook开发和维护的开源JavaScript库,主要用于构建用户界面,特别是单页应用程序(SPA)。React框架围绕组件化,即把用户界面拆分为可复用的独立组件,每个组件负…

OpenCV 4.10 发布

OpenCV 4.10 JPEG 解码速度提升 77%,实验性支持 Wayland、Win ARM64 根据 “OpenCV 中国团队” 介绍,从 4.10 开始 OpenCV 对 JPEG 图像的读取和解码有了 77% 的速度提升,超过了 scikit-image、imageio、pillow。 4.10 版本的一些亮点&…

SpringBoot+Vue甘肃非物质文化网站(前后端分离)

技术栈 JavaSpringBootMavenMySQLMyBatisVueShiroElement-UI 系统角色对应功能 用户管理员 系统功能截图

Dockerfille解析

用于构建Docker镜像的文本,由一条条指令构成 Docker执行Dockerfile的流程 1. Docker从基础镜像执行一个容器 2. 执行一条指令并对容器进行修改 3. 执行类型Docker commit的命令添加一个新的镜像层 4. Docker再基于新的镜像执行一个新的容器 5. 执行Dockerfile中…

小阿轩yx-iptables 防火墙

小阿轩yx-iptables 防火墙 Linux 防火墙基础 体系主要工作在 网络层针对TCP/IP 数据包实施过滤和限制 属于典型的包过滤防火墙(或者称为网络层防火墙) 体系基于内核编码实现 好处 具有非常稳定的性能高效率 防火墙两个表示 netfilteriptables …

C语言 数组——数组的其他应用之筛法求素数

目录 数组的其他应用 求100以内的所有素数 筛法求100以内的所有素数 自顶向下、逐步求精设计算法 数组的其他应用 求100以内的所有素数 筛法求100以内的所有素数 自顶向下、逐步求精设计算法 step 1:设计总体算法  初始化数组a,使a[2]2, a[3]3,..…

10-指针进阶——char型,多级指针,void指针,const指针

10-指针进阶——char型,多级指针,void指针,const指针 文章目录 10-指针进阶——char型,多级指针,void指针,const指针一、char 型指针1.1 示例 二、多级指针2.1 示例 三、 指针的万能拆解方法3.1 示例 四、v…