【机器学习】Django,余弦距离之基于用户,评分物品的推荐

发布时间 2023-12-08 10:56:41作者: PythonNew_Mr.Wang

表设计

# 用户表 
class UserInfo(models.Model):
    username = models.CharField(max_length=32,unique=True,verbose_name="用户名")
    password = models.CharField(max_length=64)

# 物品表
class Movies(models.Model):
    name = models.CharField(max_length=255,verbose_name="电影名称")
    movie_score =  models.FloatField(verbose_name="评分(未评分的为0)")
    
# 评分表
class UserScore(models.Model):
    movie = models.ForeignKey(to="Movies",verbose_name="电影",on_delete=models.CASCADE)
    user = models.ForeignKey(to="UserInfo",verbose_name="用户",on_delete=models.CASCADE)
    movie_score = models.FloatField(verbose_name="评分")
 

整理数据集

user_id = request.GET.get("user_id")

outList = []  # 二维数组
userIds = []  # 用户主键数组
goodsIds = []  # GOODS主键数组

# 遍历用户 - 整理数据结构成二维数组
for user_obj in models.UserInfo.objects.order_by("id"):
    userIds.append(user_obj.id)   # 储存用户ID     (找到用户主键)
    goodsIds = []  				 # 储存GOODS ID  (找到物品主键)
    innerList = []  			 # 储存特征数值   (array二维对象)
    
    # 遍历数据
    for movie_obj in models.Movies.objects.order_by("id"):
        # 储存GOODS ID
        goodsIds.append(movie_obj.id)
        try:
            # 获取用户评分
            movie_score = models.UserScore.objects.get(user_id=user_obj.id, movie_id=movie_obj).movie_score
         except:
            # 如果没有评分的物品为0
            movie_score = 0
            
            # 储存特征数值
            innerList.append(movie_score)
            outList.append(innerList)
            
                # 转换array对象
                user_ratings = np.array(outList)
                # 计算推荐ID 5个
                tj_goods_list = user_similarity_check(user_id,user_ratings, userIds, goodsIds, 5)
                # 数据库中查找
                QuerySetData = models.Movies.objects.filter(pk__in=tj_goods_list)
                # 未评分的为0
                for J in QuerySetData:
                    try:
                        J.movie_score = models.UserScore.objects.get(user_id=user_id, movie_id=J.id).movie_score
                     except:
                        J.movie_score = 0

计算相似度,推荐商品ID

# 计算用户相似度进行推荐物品ID
def user_similarity_check(user_id,user_ratings_array,userIds,goodsIds,count):
    import random
    """
        :param user_id:      当前用户ID
        :param user_ratings_array: np.array 二维数组 对象
                             user_ratings = np.array([
                                     [5, 3, 0, 0, 2],   # 用户主键ID 1 的评分
                                     [4, 0, 0, 0, 1],   # 用户主键ID 2 的评分
                                     [物品主键1, 物品主键2, 物品主键3, 物品主键4, 物品主键5],
                             ])
        :param userIds:    用户主键ID 对应每行的ID
        :param goodsIds:   物品主键ID 对应每列的ID
        :param count :     返回多少个推荐商品
        :return:        返回数组 ,包含推荐商品ID
    """
    # 计算特征相似度
    user_similarity = cosine_similarity(user_ratings_array)
    # 匹配当前用户ID 在索引里面的位置 在下面查询后直接使用
    user_index = np.where(np.array(userIds) == int(user_id))[0][0]
    # 查询出最相似的索引用户排序情况
    user_tj_list = np.argsort(-user_similarity[user_index])
    # 获取除了自身的最近的ID
    tj_user_id = user_tj_list[1]
    # 推荐GOODS
    recommended_items = user_ratings_array[tj_user_id]
    # 取出大于0的GOODS 的ID 进行推荐
    matching_indexes = np.where(recommended_items > 0)[0]
    matching_goodsIds = [goodsIds[index] for index in matching_indexes]
    return random.sample(matching_goodsIds,count)




# 计算评分相似度进行推荐物品ID
def item_similarity_check(user_id,goods_ratings_array,userIds,goodsIds,count):
    """
           :param goods_ratings_array: np.array 二维数组 对象
                                user_ratings = np.array([
                                        [5, 3, 0, 0, 2],   # 用户主键ID 1 的评分
                                        [4, 0, 0, 0, 1],   # 用户主键ID 2 的评分
                                        [物品主键1, 物品主键2, 物品主键3, 物品主键4, 物品主键5],
                                ])
           :param userIds:    用户主键ID 对应每行的ID
           :param goodsIds:   物品主键ID 对应每列的ID
           :param count :     返回多少个推荐商品
           :return:           返回数组 ,包含推荐商品ID
       """
    # 计算物品相似度
    item_similarity = cosine_similarity(goods_ratings_array.T)
    # 取出该用户对所有评分的物品 List
    user_index = np.where(np.array(userIds) == int(user_id))[0][0]
    this_user_list = goods_ratings_array[user_index]
    # 取出该用户评分为0的物品 索引列表    [5, 4, 0, 0, 2] => [2, 3]
    unrated_items = np.where(this_user_list == 0)[0]
    #             物品之间的相似度   该用户对物品的评分   ====>    计算两个数组的点积(即矩阵乘法)
    top = np.dot(item_similarity, goods_ratings_array[user_index]) 
    # 包含所有物品的预测评分的数组(或向量),其中每个元素表示该物品的预测评分。
    
    
    # 包含每个物品的相似度之和的数组(或向量),其中每个元素表示每个物品与其他所有物品的相似度之和
    down = np.sum(item_similarity, axis=1)  
    # 计算用户对未评分物品的兴趣度
    scores = top / down  
    # 计算用户对未评分物品的兴趣度
    top_items = [x for x in sorted(zip(unrated_items, scores[unrated_items]), key=lambda x: x[1], reverse=True)]
    # 获取索引
    tj_goods_id_list = [x[0] for x in top_items[0:count]]
    matching_elements = [goodsIds[index] for index in tj_goods_id_list]
    return matching_elements