音乐推荐系统

2019-07-17  本文已影响0人  潇萧之炎
# coding: utf-8

# In[1]:

import numpy as np
import json
import random
import time
import pickle


def random_sample_playlist(in_file, out_file, max_sample=10, seed=None):
    """
    从给定的输入文件in_file中提取出最多max_sample条样本数据,并将其输入到out_file文件中
    :param in_file:
    :param out_file:
    :param max_sample:
    :param seed:
    :return:
    """
    # 设置给定的随机数种子
    if seed:
        random.seed(seed)

    # 进行数据的随机抽取过程
    with open(out_file, 'w', encoding='utf-8') as writer:
        with open(in_file, 'r', encoding='utf-8') as reader:
            count = 0
            for line in reader:
                if random.random() < 0.8:
                    writer.writelines(line)
                    count += 1
                    if count % 100 == 0:
                        print("已经抽取%d个歌单数据!!" % count)

                    if count >= max_sample:
                        break
            print("实际抽取的总歌单数据为:%d条" % count)


def parse_playlist_2_song(playlist_json):
    """
    将传入的json格式的歌单数据转换为具体的特征数据
    :param playlist_json:
    :return:
    """
    try:
        # 将字符串转换为json对象
        data = json.loads(playlist_json)
        print('data',data)
        # 开始获取数据
        result_data = data['result']
        # 1. 获取歌单数据(用户id,歌单id,歌单名称,最近的更新时间,订阅数,播放数)
        user_id = result_data['userId']
        playlist_id = result_data['id']
        playlist_name = result_data['name'].replace('\t', '')
        playlist_subscribed_count = result_data['subscribedCount']
        playlist_play_count = result_data['playCount']
        playlist_update_time = result_data['updateTime']
        # 2. 获取歌曲信息
        song_info = ''
        songs = result_data['tracks']
        for song in songs:
            # 歌曲id,歌曲名称,歌曲热度
            song_id = song['id']
            song_name = song['name'].replace('\t', '')
            song_popularity = song['popularity']
            song_info += '\t' + '::::'.join([str(song_id), song_name, str(song_popularity)])

        return '##'.join(
            [str(user_id), str(playlist_id), playlist_name, str(playlist_update_time), str(playlist_subscribed_count),
             str(playlist_play_count)]) + song_info
    except Exception as e:
        return False


def parse_playlist_file(in_file, out_file):
    """
    从给定的歌单原始数据文件中提取有关的特征数据,并将其保存到给定的输出文件中
    :param in_file:
    :param out_file:
    :return:
    """
    with open(out_file, 'w', encoding='utf-8') as writer:
        with open(in_file, 'r', encoding='utf-8') as reader:
            for line in reader:
                # 1. 对当前的歌单数据line进行处理
                result = parse_playlist_2_song(line)

                # 2. 对于处理的结果进行判断,如果处理成功并且有值,那么直接输出
                if result:
                    writer.writelines(result)
                    writer.writelines('\n')
                else:
                    print("提取歌单主要特征数据失败:{}".format(line))


def is_last_time(update_time):
    return int(time.time()) * 1000 - update_time < 31536000000


def parse_user_song_rating(user_id, update_time, subscribed_count, play_count, song_info):
    """
    基于输入的参数,构建一个评分
    :param user_id:
    :param update_time:
    :param subscribed_count:
    :param play_count:
    :param song_info:
    :return:
    """
    try:
        # 提取特征
        song_id, _, song_popularity = song_info.split('::::')

        # 计算当前用户对于单个歌曲的评分
        # TODO: 由于我们的数据原因,没法直接获取用用户对于歌曲的评分信息,那么将歌曲的热度作为评分值,并且结合播放次数、订阅次数、更新时间等做一个加权变化;并且我们只要这个歌单中存在这个歌曲,那么表示这个人对于这个歌曲是比较偏好的,所以评分一定是正向的,假设评分的取值范围[1~10], 那么认为原始数据中的评分一定是超过5分的
        w = 1.0
        if play_count > 10000 and subscribed_count > 1000 and is_last_time(update_time):
            w = 1.1
        elif play_count <= 10000 and subscribed_count <= 1000 and (not is_last_time(update_time)):
            w = 0.9
        rating = float(song_popularity) * w
        # 首先将评分截断为[1,10], 如果评分超过10分,那么设置为10;如果低于1分,那么设置为1
        rating = np.clip(rating / 10, 1.0, 10.0)
        # 认为数据实在存在,那么一定是喜好的,所以评分一定是超过5分的
        rating = ((rating - 1.0) / 9.0) * 5.0 + 5.0

        return ','.join([user_id, song_id, str(rating)])
    except:
        return ''


def parse_user_song_rating_file(in_file, out_file):
    """
    对输入的特征属性的文件内容进行用户-歌曲评分的构建,并将结果保存到out_file
    :param in_file:
    :param out_file:
    :return:
    """
    with open(out_file, 'w', encoding='utf-8') as writer:
        with open(in_file, 'r', encoding='utf-8') as reader:
            for line in reader:
                # 获取歌单信息和歌单中的歌曲数据
                contents = line.split('\t')
                playlist_content, song_contents = contents[0], contents[1:]
                # 获取具体的歌单数据
                user_id, _, _, update_time, subscribed_count, play_count = playlist_content.split("##")
                update_time = float(update_time)
                subscribed_count = float(subscribed_count)
                play_count = float(play_count)
                # 获取当前用户user_id对于所有songs的分别的评分信息(user-song-rating)
                user_song_info = map(
                    lambda song: parse_user_song_rating(user_id, update_time, subscribed_count, play_count, song),
                    song_contents)
                user_song_info = filter(lambda t: len(t.split(',')) == 3, user_song_info)

                # 输出数据
                result = '\n'.join(user_song_info)
                if result:
                    writer.writelines(result)
                    writer.writelines('\n')


def parse_playlist_song_rating(playlist_id, song_info):
    """
    基于给定的数据,得到歌单-歌曲的评分值
    :param playlist_id:
    :param song_info:
    :return:
    """
    try:
        # 提取歌曲信息
        song_id, _, _ = song_info.split('::::')

        # 在这里计算的评分矩阵主要是为了实现需求:计算歌单之间的相似度,然后将相似歌单作为当前歌单的推荐数据
        # 可以认为如果两个歌单中,出现的歌曲的重复度越高,那么这两个歌单就越相似度;也就是说只要这两个歌单中的歌曲列表是一致的,那么就可以认为是歌单的相似的---->基于jaccard相似度计算 ---> 不需要考虑具体的评分值
        rating = 1.0

        return ','.join([playlist_id, song_id, str(rating)])
    except:
        return ''


def parse_playlist_song_rating_file(in_file, out_file):
    """
    构建歌单-歌曲之间的评分矩阵
    :param in_file:
    :param out_file:
    :return:
    """
    with open(out_file, 'w', encoding='utf-8') as writer:
        with open(in_file, 'r', encoding='utf-8') as reader:
            for line in reader:
                # 获取歌单信息和歌单中的歌曲数据
                contents = line.split('\t')
                playlist_content, song_contents = contents[0], contents[1:]
                # 获取具体的歌单数据
                _, playlist_id, _, _, _, _ = playlist_content.split("##")
                # 获取当前歌单playlist_id对于所有songs的分别的评分信息(playlist-song-rating)
                playlist_song_info = map(lambda song: parse_playlist_song_rating(playlist_id, song), song_contents)
                playlist_song_info = filter(lambda t: len(t.split(',')) == 3, playlist_song_info)

                # 输出数据
                result = '\n'.join(playlist_song_info)
                if result:
                    writer.writelines(result)
                    writer.writelines('\n')


def parse_playlist_song_id_2_name(in_file, out_playlist, out_song):
    """
    提取id和name映射关系到文件中
    :param in_file:
    :param out_playlist:
    :param out_song:
    :return:
    """
    # 歌单id和歌单名称的映射字典
    playlist_id_2_name = {}
    # 歌曲id和歌曲名称的映射字典
    song_id_2_name = {}

    # 从输入数据中进行处理
    with open(in_file, 'r', encoding='utf-8') as reader:
        for line in reader:
            try:
                # 划分数据
                contents = line.split("\t")
                try:
                    # 提取歌单id和歌单名称
                    _, playlist_id, playlist_name, _, _, _ = contents[0].split('##')
                    playlist_id_2_name[playlist_id] = playlist_name
                    # 提取歌曲id和歌曲名称
                    for song in contents[1:]:
                        try:
                            song_id, song_name, _ = song.split("::::")
                            song_id_2_name[song_id] = song_name
                        except:
                            print("Fetch the song id throw error:{}".format(song))
                except:
                    print("Fetch the playlist id throw error:{}".format(contents[0]))
            except:
                print("Fetch error!")

    # 输出数据
    with open(out_playlist, 'wb') as playlist_writer:
        pickle.dump(playlist_id_2_name, playlist_writer)
    with open(out_song, 'wb') as song_writer:
        pickle.dump(song_id_2_name, song_writer)


if __name__ == '__main__':
    # all_music_playlist_file_path = 'D:/music/playlist_detail_music_all.json'
    all_music_playlist_file_path = 'C:/我的电脑/文件下载/第五期推荐系统循环课/[20190118]_推荐系统:推荐系统概述/playlist_detail_music_all/playlist_detail_music_all.json'
    # sample_playlist_file_pth = 'D:/music/playlist_detail_music_500.json'
    sample_playlist_file_pth = 'C:/我的电脑/Python/推荐系统/数据/playlist_detail_music_500.json'
    music_playlist_song_file_path = 'C:/我的电脑/Python/推荐系统/数据/163_music_playlist_song.txt'
    music_user_song_rating_file_path = 'C:/我的电脑/Python/推荐系统/数据/163_music_user_song_rating.txt'
    music_playlist_song_rating_file_path = 'C:/我的电脑/Python/推荐系统/数据/163_music_playlist_song_rating.txt'
    playlist_id_2_name_file_path = 'C:/我的电脑/Python/推荐系统/数据/163_playlist_id_2_name.pkl'
    song_id_2_name_file_path = 'C:/我的电脑/Python/推荐系统/数据/163_song_id_2_name.pkl'

    # 1. 样本数据的抽取
    random_sample_playlist(all_music_playlist_file_path, sample_playlist_file_pth, 500)

    # 2. 特征属性的提取
    parse_playlist_file(sample_playlist_file_pth, music_playlist_song_file_path)

    # 3. 构建用户-歌曲评分矩阵
    parse_user_song_rating_file(music_playlist_song_file_path, music_user_song_rating_file_path)

    # 4. 构建歌单-歌曲评分矩阵
    parse_playlist_song_rating_file(music_playlist_song_file_path, music_playlist_song_rating_file_path)

    # 5. 提取id和name的映射
    parse_playlist_song_id_2_name(music_playlist_song_file_path, playlist_id_2_name_file_path, song_id_2_name_file_path)

# -- encoding:utf-8 --
"""
模拟离线的模型训练过程
需求一:每天为每个用户产生20首歌曲的一个推荐列表,要求这二十首歌中不能出现该用户实际偏好的歌曲
需求二:当用户浏览某个歌单页面,给该用户推荐和当前歌单比较类似的5个其它歌单
TODO: 作业 --> 将保存磁盘的代码更改为保存数据库的代码,并且对推荐结果的应用在去稍微的理解理解。
Create by ibf on 19/1/23
"""

import pickle
import pymysql
import surprise
from surprise import KNNBaseline, SVD
from surprise import Dataset, Reader


def train_model1(in_file, out_file):
    """
    针对需求一,使用给定的MovieLens格式的输入数据,进行模型构建,并将模型保存到对应的磁盘文件中
    :param in_file:
    :param out_file:
    :return:
    """
    # 1. 数据加载
    reader = Reader(line_format='user item rating', sep=',', rating_scale=(1, 10))
    data = Dataset.load_from_file(file_path=in_file, reader=reader)

    # 2. 数据转换
    trainset = data.build_full_trainset()

    # 3. 模型构建
    algo = SVD(n_factors=100, n_epochs=200, reg_all=0.2, lr_all=0.1)

    # 4. 模型训练
    algo.fit(trainset)

    # 5. 模型持久化
    surprise.dump.dump(out_file, algo=algo)


def fetch_recommend_list_by_model1_2_file(model_in_file, out_file, song_id_2_name, K=20):
    """
    基于给定的模型,进行推荐列表的获取,并将推荐列表保存到磁盘中
    :param model_in_file:  模型文件
    :param out_file:  输出文件路径
    :param song_id_2_name: 给定的一个词典,中间是歌曲id和歌曲名称的一个映射
    :param K:  给定具体获取多少个推荐歌曲
    :return:
    """
    # 1. 加载模型
    _, algo = surprise.dump.load(model_in_file)

    # 2. 获取所有用户以及所有物品,并且对所有用户产生一个推荐列表,并将最终的推荐结果保存磁盘
    result = {}
    # a. 获取用户的总数目
    total_users = algo.trainset.n_users
    # b. 获取物品的总数目
    total_items = algo.trainset.n_items
    print("总用户数目:{}, 总物品数目:{}".format(total_users, total_items))
    # c. 遍历所有用户以及所有物品产生推荐列表
    for inner_user_id in range(total_users):
        print("开始处理用户:{}".format(inner_user_id))
        # -0. 初始化相关参数
        top_k_list = []
        count = 0
        # 获取当前用户评分过的商品id所组成的列表
        user_2_song_id_list = list(map(lambda t: t[0], algo.trainset.ur[inner_user_id]))

        # -1. 将内部用户id转换为实际用户id
        raw_user_id = algo.trainset.to_raw_uid(inner_user_id)

        # -2. 获取当前用户对于所有物品的评分,然后获取评分最高的K个商品以及商品评分保存到列表中
        for inner_item_id in range(total_items):
            # 只有当当前物品不在用户的评分物品列表中的时候,我才计算预测评分信息。
            if inner_item_id not in user_2_song_id_list:
                # -a. 将内部的物品id转换为实际的物品id
                raw_item_id = algo.trainset.to_raw_iid(inner_item_id)
                # -b. 计算当前用户对于当前物品的评分
                rating = algo.predict(raw_user_id, raw_item_id).est
                # -c. 将当前用户对于当前物品的评分添加到临时列表中
                # NOTE: top_k_list中最多保存rating最高的K个商品(如果列表中商品数目小于K个,那么当前商品直接添加;如果列表中商品数目多余K个,那么就根据大小进行操作)
                if count < K:
                    top_k_list.append((raw_item_id, rating))
                    count += 1
                else:
                    # --1. 对top_k_list做一个排序(按照rating评分排序)
                    top_k_list.sort(key=lambda t: t[1])
                    # --2. 获取top_k_list中评分最小的评分值(因为做了一个升序排列,最小的在第一个位置)
                    min_rating = top_k_list[0][1]
                    # --3. 如果当前的评分大于最小评分,那么最小评分所对应的商品从列表中删除,然后添加当前商品信息
                    if rating > min_rating:
                        top_k_list[0] = (raw_item_id, rating)

        # -3. 将获取得到的推荐列表保存到字典中
        top_k_list.sort(key=lambda t: t[1], reverse=True)
        top_k_list = list(map(lambda t: (t[0], song_id_2_name[t[0]], t[1]), top_k_list))
        result[raw_user_id] = top_k_list

    # d. 将推荐结果保存到磁盘文件中
    with open(out_file, 'wb') as writer:
        pickle.dump(result, writer)


def fetch_recommend_list_by_model1_2_mysql(model_in_file, K=20):
    """
    基于给定的模型,进行推荐列表的获取,并将推荐列表保存到磁盘中
    :param model_in_file:  模型文件
    :param K:  给定具体获取多少个推荐歌曲
    :return:
    """
    # 1. 加载模型
    _, algo = surprise.dump.load(model_in_file)

    # 数据持久化输出到数据库中
    delete_sql = "delete from tb_user_recommend_song"
    sql = "INSERT INTO tb_user_recommend_song(`user_id`, `song_id`, `rating`) values(%d, %d, %.4f)"
    try:
        with pymysql.connect(host='localhost', user='root', password='root',
                             port=3306, database='test', charset='utf8') as cursor:
            # 清空数据库中的所有数据
            cursor.execute(delete_sql)

            # 2. 获取所有用户以及所有物品,并且对所有用户产生一个推荐列表,并将最终的推荐结果保存磁盘
            # a. 获取用户的总数目
            total_users = algo.trainset.n_users
            # b. 获取物品的总数目
            total_items = algo.trainset.n_items
            print("总用户数目:{}, 总物品数目:{}".format(total_users, total_items))
            # c. 遍历所有用户以及所有物品产生推荐列表
            for inner_user_id in range(total_users):
                print("开始处理用户:{}".format(inner_user_id))
                # -0. 初始化相关参数
                top_k_list = []
                count = 0
                # 获取当前用户评分过的商品id所组成的列表
                user_2_song_id_list = list(map(lambda t: t[0], algo.trainset.ur[inner_user_id]))

                # -1. 将内部用户id转换为实际用户id
                raw_user_id = algo.trainset.to_raw_uid(inner_user_id)

                # -2. 获取当前用户对于所有物品的评分,然后获取评分最高的K个商品以及商品评分保存到列表中
                for inner_item_id in range(total_items):
                    # 只有当当前物品不在用户的评分物品列表中的时候,我才计算预测评分信息。
                    if inner_item_id not in user_2_song_id_list:
                        # -a. 将内部的物品id转换为实际的物品id
                        raw_item_id = algo.trainset.to_raw_iid(inner_item_id)
                        # -b. 计算当前用户对于当前物品的评分
                        rating = algo.predict(raw_user_id, raw_item_id).est
                        # -c. 将当前用户对于当前物品的评分添加到临时列表中
                        # NOTE: top_k_list中最多保存rating最高的K个商品(如果列表中商品数目小于K个,那么当前商品直接添加;如果列表中商品数目多余K个,那么就根据大小进行操作)
                        if count < K:
                            top_k_list.append((raw_item_id, rating))
                            count += 1
                        else:
                            # --1. 对top_k_list做一个排序(按照rating评分排序)
                            top_k_list.sort(key=lambda t: t[1])
                            # --2. 获取top_k_list中评分最小的评分值(因为做了一个升序排列,最小的在第一个位置)
                            min_rating = top_k_list[0][1]
                            # --3. 如果当前的评分大于最小评分,那么最小评分所对应的商品从列表中删除,然后添加当前商品信息
                            if rating > min_rating:
                                top_k_list[0] = (raw_item_id, rating)

                # -3. 将获取得到的推荐列表保存到数据库中
                int_user_id = int(raw_user_id)
                for song_id, rating in top_k_list:
                    cursor.execute(sql % (int_user_id, int(song_id), rating))
    except Exception as e:
        print("ERROR!!!{}".format(e))
        cursor.connection.rollback()


def train_model2(in_file, out_file):
    """
    针对需求二,使用给定的MovieLens格式的输入数据,进行模型构建,并将模型保存到对应的磁盘文件中
    :param in_file:
    :param out_file:
    :return:
    """
    # 1. 数据加载
    reader = Reader(line_format='user item rating', sep=',', rating_scale=(1, 1))
    data = Dataset.load_from_file(file_path=in_file, reader=reader)

    # 2. 数据转换
    trainset = data.build_full_trainset()

    # 3. 模型构建
    sim_options = {
        'name': 'jaccard',  # 因为评分数据只有1分这个值,我们认为只要歌单(用户)具有相同的歌曲(物品),那么就认为歌单(用户)相似度 --> 所以不需要考虑具体的分值
        'user_based': True
    }
    algo = KNNBaseline(k=10, sim_options=sim_options)

    # 4. 模型训练
    algo.fit(trainset)

    # 5. 模型持久化
    surprise.dump.dump(out_file, algo=algo)


def fetch_recommend_list_by_model2_2_file(model_in_file, out_file, playlist_id_2_name, K=5):
    """
    基于给定的模型,进行推荐列表的获取,并将推荐列表保存到磁盘中
    :param model_in_file:  模型文件
    :param out_file:  输出文件路径
    :param playlist_id_2_name: 给定的一个词典,中间是歌单id和歌单名称的一个映射
    :param K:  给定具体获取多少个推荐歌单
    :return:
    """
    # 1. 加载模型
    _, algo = surprise.dump.load(model_in_file)

    # 2. 获取所有用户,并且对所有用户产生一个推荐列表<基于用户的相似度产生>,并将最终的推荐结果保存磁盘
    # NOTE: 用户即歌单
    result = {}
    # a. 获取用户/歌单的总数目
    total_playlist = algo.trainset.n_users
    print("总歌单数目:{}".format(total_playlist))
    # c. 遍历所有用户/歌单,获取和当前歌单最相似的K个歌单作为推荐结果
    for inner_playlist_id1 in range(total_playlist):
        print("开始处理歌单:{}".format(inner_playlist_id1))

        # -0. 初始化相关参数
        top_k_list = []
        count = 0

        # -1. 将内部歌单id转换为实际歌单id
        raw_playlist_id1 = algo.trainset.to_raw_uid(inner_playlist_id1)

        # -2. 从相似度矩阵中获取和当前歌单最相似的K个歌单保存到集合中
        for inner_playlist_id2 in range(total_playlist):
            # 内外两个歌单id不能一样
            if inner_playlist_id1 != inner_playlist_id2:
                # -a. 将内部歌单id转换为实际歌单id
                raw_playlist_id2 = algo.trainset.to_raw_uid(inner_playlist_id2)
                # -b. 获取两个歌单的相似度
                sim = algo.sim[inner_playlist_id1, inner_playlist_id2]
                # -c. 将当前两个歌单的相似度添加到临时列表中
                # NOTE: top_k_list中最多保存sim最高的K个歌单(如果列表中歌单数目小于K个,那么当前歌单直接添加;如果列表中歌单数目多余K个,那么就根据大小进行操作)
                if count < K:
                    top_k_list.append((raw_playlist_id2, sim))
                    count += 1
                else:
                    # --1. 对top_k_list做一个排序(按照sim评分排序)
                    top_k_list.sort(key=lambda t: t[1])
                    # --2. 获取top_k_list中sim最小的sim值(因为做了一个升序排列,最小的在第一个位置)
                    min_sim = top_k_list[0][1]
                    # --3. 如果当前的sim大于最小sim,那么最小sim所对应的歌单从列表中删除,然后添加当前歌单信息
                    if sim > min_sim:
                        top_k_list[0] = (raw_playlist_id2, sim)

        # -3. 将获取得到的推荐列表保存到字典中
        top_k_list.sort(key=lambda t: t[1], reverse=True)
        top_k_list = list(map(lambda t: (t[0], playlist_id_2_name[t[0]], t[1]), top_k_list))
        result[raw_playlist_id1] = top_k_list

    # d. 将推荐结果保存到磁盘文件中
    with open(out_file, 'wb') as writer:
        pickle.dump(result, writer)


def fetch_recommend_list_by_model2_2_mysql(model_in_file, K=5):
    """
    基于给定的模型,进行推荐列表的获取,并将推荐列表保存到磁盘中
    :param model_in_file:  模型文件
    :param out_file:  输出文件路径
    :param playlist_id_2_name: 给定的一个词典,中间是歌单id和歌单名称的一个映射
    :param K:  给定具体获取多少个推荐歌单
    :return:
    """
    # 1. 加载模型
    _, algo = surprise.dump.load(model_in_file)

    delete_sql = "delete from tb_playlist_recommend_playlist"
    sql = "INSERT INTO tb_playlist_recommend_playlist(`playlist_id`, `playlist_id2`, `sim`) values(%d, %d, %.4f)"
    try:
        with pymysql.connect(host='localhost', user='root', password='root',
                             port=3306, database='test', charset='utf8') as cursor:
            # 清空数据库中的所有数据
            cursor.execute(delete_sql)

            # 2. 获取所有用户,并且对所有用户产生一个推荐列表<基于用户的相似度产生>,并将最终的推荐结果保存磁盘
            # NOTE: 用户即歌单
            # a. 获取用户/歌单的总数目
            total_playlist = algo.trainset.n_users
            print("总歌单数目:{}".format(total_playlist))
            # c. 遍历所有用户/歌单,获取和当前歌单最相似的K个歌单作为推荐结果
            for inner_playlist_id1 in range(total_playlist):
                print("开始处理歌单:{}".format(inner_playlist_id1))

                # -0. 初始化相关参数
                top_k_list = []
                count = 0

                # -1. 将内部歌单id转换为实际歌单id
                raw_playlist_id1 = algo.trainset.to_raw_uid(inner_playlist_id1)

                # -2. 从相似度矩阵中获取和当前歌单最相似的K个歌单保存到集合中
                for inner_playlist_id2 in range(total_playlist):
                    # 内外两个歌单id不能一样
                    if inner_playlist_id1 != inner_playlist_id2:
                        # -a. 将内部歌单id转换为实际歌单id
                        raw_playlist_id2 = algo.trainset.to_raw_uid(inner_playlist_id2)
                        # -b. 获取两个歌单的相似度
                        sim = algo.sim[inner_playlist_id1, inner_playlist_id2]
                        # -c. 将当前两个歌单的相似度添加到临时列表中
                        # NOTE: top_k_list中最多保存sim最高的K个歌单(如果列表中歌单数目小于K个,那么当前歌单直接添加;如果列表中歌单数目多余K个,那么就根据大小进行操作)
                        if count < K:
                            top_k_list.append((raw_playlist_id2, sim))
                            count += 1
                        else:
                            # --1. 对top_k_list做一个排序(按照sim评分排序)
                            top_k_list.sort(key=lambda t: t[1])
                            # --2. 获取top_k_list中sim最小的sim值(因为做了一个升序排列,最小的在第一个位置)
                            min_sim = top_k_list[0][1]
                            # --3. 如果当前的sim大于最小sim,那么最小sim所对应的歌单从列表中删除,然后添加当前歌单信息
                            if sim > min_sim:
                                top_k_list[0] = (raw_playlist_id2, sim)

                # -3. 将获取得到的推荐列表保存到数据库中
                int_playlist_id = int(raw_playlist_id1)
                for playlist_id, sim in top_k_list:
                    cursor.execute(sql % (int_playlist_id, int(playlist_id), sim))

    except Exception as e:
        print("ERROR!!!{}".format(e))
        cursor.connection.rollback()


if __name__ == '__main__':
    music_user_song_rating_file_path = 'D:/music/163_music_user_song_rating.txt'
    music_playlist_song_rating_file_path = 'D:/music/163_music_playlist_song_rating.txt'
    playlist_id_2_name_file_path = 'D:/music/163_playlist_id_2_name.pkl'
    song_id_2_name_file_path = 'D:/music/163_song_id_2_name.pkl'
    model1_file_path = 'D:/music/model/m1.m'
    model2_file_path = 'D:/music/model/m2.m'
    model1_result_file_path = 'D:/music/model/r1.data'
    model2_result_file_path = 'D:/music/model/r2.data'
    train_model_flag = 2

    if train_model_flag == 0:
        print("进行模型训练.....")
        print("开始需求一模型训练....")
        train_model1(music_user_song_rating_file_path, model1_file_path)
        print("开始需求二模型训练....")
        train_model2(music_playlist_song_rating_file_path, model2_file_path)
        print("模型训练完成!!!")
    elif train_model_flag == 1:
        print("进行推荐列表的获取操作(保存磁盘)....")
        song_id_2_name = pickle.load(open(song_id_2_name_file_path, 'rb'))
        playlist_id_2_name = pickle.load(open(playlist_id_2_name_file_path, 'rb'))
        print(playlist_id_2_name.keys())
        print("映射关系加载完成!!!")
        print("开始产生需求一的推荐列表....")
        fetch_recommend_list_by_model1_2_file(model1_file_path, model1_result_file_path, song_id_2_name)
        print("开始产生需求二的推荐列表....")
        fetch_recommend_list_by_model2_2_file(model2_file_path, model2_result_file_path, playlist_id_2_name)
        print("推荐数据产生完成!!!!")
    elif train_model_flag == 2:
        print("进行推荐列表的生成操作(保存数据库)....")
        print("开始产生需求一的推荐列表....")
        fetch_recommend_list_by_model1_2_mysql(model1_file_path)
        print("开始产生需求二的推荐列表....")
        fetch_recommend_list_by_model2_2_mysql(model2_file_path)
        print("推荐数据产生完成!!!!")

上一篇 下一篇

猜你喜欢

热点阅读