02 推荐算法-(02) 基于内容的推荐算法(Content-Based)

文章介绍了基于内容的推荐算法,特别是如何使用TF-IDF来构建电影的物品画像,通过分析电影标签和用户观影历史生成用户画像,然后通过物品间的相似度计算进行推荐。此外,还讨论了如何处理物品的冷启动问题,以及如何使用Word2Vec和Doc2Vec模型进一步增强推荐系统的性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于内容的推荐算法(Content-Based)

简介

基于内容的推荐方法是非常直接的,它以物品的内容描述信息为依据来做出的推荐,本质上是基于对物品和用户自身的特征或属性的直接分析和计算。

例如,假设已知电影A是一部喜剧,而恰巧我们得知某个用户喜欢看喜剧电影,那么我们基于这样的已知信息,就可以将电影A推荐给该用户。

  • 画像构建。顾名思义,画像就是刻画物品或用户的特征。本质上就是给用户或物品贴标签。

    • 物品画像:例如给电影《战狼2》贴标签,可以有哪些?
      在这里插入图片描述

      “动作”、“吴京”、“吴刚”、“张翰”、“大陆电影”、“国产”、“爱国”、"军事"等等一系列标签是不是都可以贴上

    • 用户画像:例如已知用户的观影历史是:“《战狼1》”、“《战狼2》”、“《建党伟业》”、“《建军大业》”、“《建国大业》”、“《红海行动》”、"《速度与激情1-8》"等,我们是不是就可以分析出该用户的一些兴趣特征如:“爱国”、“战争”、“赛车”、“动作”、“军事”、“吴京”、"韩三平"等标签。

问题:物品的标签来自哪儿?

  1. PGC 物品画像–冷启动
    • 物品自带的属性(物品一产生就具备的):如电影的标题、导演、演员、类型等等
    • 服务提供方设定的属性(服务提供方为物品附加的属性):如短视频话题、微博话题(平台拟定)
    • 其他渠道:如爬虫
  2. UGC 冷启动问题
    • 用户在享受服务过程中提供的物品的属性:如用户评论内容,微博话题(用户拟定)

根据PGC内容构建的物品画像的可以解决物品的冷启动问题

基于内容推荐的算法流程

  • 根据PGC/UGC内容构建物品画像
  • 根据用户行为记录生成用户画像
  • 根据用户画像从物品中寻找最匹配的TOP-N物品进行推荐

物品冷启动处理

  • 根据PGC内容构建物品画像
  • 利用物品画像计算物品间两两相似情况
  • 为每个物品产生TOP-N最相似的物品进行相关推荐:如与该商品相似的商品有哪些?与该文章相似文章有哪些?

基于内容的电影推荐:物品画像

物品画像构建步骤

  • 利用tags.csv中每部电影的标签作为电影的候选关键词
  • 利用TF·IDF计算每部电影的标签的tfidf值,选取TOP-N个关键词作为电影画像标签
  • 将电影的分类词直接作为每部电影的画像标签

基于TF-IDF的特征提取技术

前面提到,物品画像的特征标签主要都是指的如电影的导演、演员、图书的作者、出版社等结构话的数据,也就是他们的特征提取,尤其是体征向量的计算是比较简单的,如直接给作品的分类定义0或者1的状态。

但另外一些特征,比如电影的内容简介、电影的影评、图书的摘要等文本数据,这些被称为非结构化数据,首先他们本应该也属于物品的一个特征标签,但是这样的特征标签进行量化时,也就是计算它的特征向量时是很难去定义的。

因此这时就需要借助一些自然语言处理、信息检索等技术,将如用户的文本评论或其他文本内容信息的非结构化数据进行量化处理,从而实现更加完善的物品画像/用户画像。

TF-IDF算法便是其中一种在自然语言处理领域中应用比较广泛的一种算法。可用来提取目标文档中,并得到关键词用于计算对于目标文档的权重,并将这些权重组合到一起得到特征向量。

算法原理

TF-IDF自然语言处理领域中计算文档中词或短语的权值的方法,是词频(Term Frequency,TF)和逆转文档频率(Inverse Document Frequency,IDF)的乘积。TF指的是某一个给定的词语在该文件中出现的次数。这个数字通常会被正规化,以防止它偏向长的文件(同一个词语在长文件里可能会比短文件有更高的词频,而不管该词语重要与否)。IDF是一个词语普遍重要性的度量,某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到。

TF-IDF算法基于一个这样的假设:若一个词语在目标文档中出现的频率高而在其他文档中出现的频率低,那么这个词语就可以用来区分出目标文档。这个假设需要掌握的有两点:

  • 在本文档出现的频率高;
  • 在其他文档出现的频率低。

因此,TF-IDF算法的计算可以分为词频(Term Frequency,TF)和逆转文档频率(Inverse Document Frequency,IDF)两部分,由TF和IDF的乘积来设置文档词语的权重。

TF指的是一个词语在文档中的出现频率。假设文档集包含的文档数为 N N N,文档集中包含关键词 k i k_i ki的文档数为 n i n_i ni f i j f_{ij} fij表示关键词 k i k_i ki在文档 d j d_j dj中出现的次数, f d j f_{dj} fdj表示文档 d j d_j dj中出现的词语总数, k i k_i ki在文档dj中的词频 T F i j TF_{ij} TFij定义为:
T F i j = f i j f d j TF_{ij}=\frac {f_{ij}}{f_{dj}} TFij=fdjfij
并且注意,这个数字通常会被正规化,以防止它偏向长的文件(指同一个词语在长文件里可能会比短文件有更高的词频,而不管该词语重要与否)。

IDF是一个词语普遍重要性的度量。表示某一词语在整个文档集中出现的频率,由它计算的结果取对数得到关键词 k i k_i ki的逆文档频率 I D F i IDF_i IDFi I D F i = l o g N n i IDF_i=log\frac {N}{n_i} IDFi=logniN

由TF和IDF计算词语的权重为:
w i j = T F i j w_{ij}=TF_{ij} wij=TFij
I D F i = f i j f d j IDF_{i}=\frac {f_{ij}}{f_{dj}} IDFi=fdjfij
l o g N n i log\frac {N}{n_i} logniN

结论:TF-IDF与词语在文档中的出现次数成正比,与该词在整个文档集中的出现次数成反比。

用途:在目标文档中,提取关键词(特征标签)的方法就是将该文档所有词语的TF-IDF计算出来并进行对比,取其中TF-IDF值最大的k个数组成目标文档的特征向量用以表示文档。

注意:文档中存在的停用词(Stop Words),如“是”、“的”之类的,对于文档的中心思想表达没有意义的词,在分词时需要先过滤掉再计算其他词语的TF-IDF值。

算法举例

对于计算影评的TF-IDF,以电影“加勒比海盗:黑珍珠号的诅咒”为例,假设它总共有1000篇影评,其中一篇影评的总词语数为200,其中出现最频繁的词语为“海盗”、“船长”、“自由”,分别是20、15、10次,并且这3个词在所有影评中被提及的次数分别为1000、500、100,就这3个词语作为关键词的顺序计算如下。

  1. 将影评中出现的停用词过滤掉,计算其他词语的词频。以出现最多的三个词为例进行计算如下:

    • “海盗”出现的词频为20/200=0.1
    • “船长”出现的词频为15/200=0.075
    • “自由”出现的词频为10/200=0.05;
  2. 计算词语的逆文档频率如下:

    • “海盗”的IDF为:log(1000/1000)=0
    • “船长”的IDF为:log(1000/500)=0.3
      “自由”的IDF为:log(1000/100)=1
  3. 由1和2计算的结果求出词语的TF-IDF结果,“海盗”为0,“船长”为0.0225,“自由”为0.05。

通过对比可得,该篇影评的关键词排序应为:“自由”、“船长”、“海盗”。把这些词语的TF-IDF值作为它们的权重按照对应的顺序依次排列,就得到这篇影评的特征向量,我们就用这个向量来代表这篇影评,向量中每一个维度的分量大小对应这个属性的重要性。

将总的影评集中所有的影评向量与特定的系数相乘求和,得到这部电影的综合影评向量,与电影的基本属性结合构建视频的物品画像,同理构建用户画像,可采用多种方法计算物品画像和用户画像之间的相似度,为用户做出推荐。

加载数据集

import pandas as pd
import numpy as np
'''
- 利用tags.csv中每部电影的标签作为电影的候选关键词
- 利用TF·IDF计算每部电影的标签的tfidf值,选取TOP-N个关键词作为电影画像标签
- 并将电影的分类词直接作为每部电影的画像标签
'''

def get_movie_dataset():
    # 加载基于所有电影的标签
    # all-tags.csv来自ml-latest数据集中
    # 由于ml-latest-small中标签数据太多,因此借助其来扩充
    _tags = pd.read_csv("datasets/ml-latest-small/all-tags.csv", usecols=range(1, 3)).dropna()
    tags = _tags.groupby("movieId").agg(list)

    # 加载电影列表数据集
    movies = pd.read_csv("datasets/ml-latest-small/movies.csv", index_col="movieId")
    # 将类别词分开
    movies["genres"] = movies["genres"].apply(lambda x: x.split("|"))
    # 为每部电影匹配对应的标签数据,如果没有将会是NAN
    movies_index = set(movies.index) & set(tags.index)
    new_tags = tags.loc[list(movies_index)]
    ret = movies.join(new_tags)

    # 构建电影数据集,包含电影Id、电影名称、类别、标签四个字段
    # 如果电影没有标签数据,那么就替换为空列表
    # map(fun,可迭代对象)
    movie_dataset = pd.DataFrame(
        map(
            lambda x: (x[0], x[1], x[2], x[2]+x[3]) if x[3] is not np.nan else (x[0], x[1], x[2], []), ret.itertuples())
        , columns=["movieId", "title", "genres","tags"]
    )

    movie_dataset.set_index("movieId", inplace=True)
    return movie_dataset

movie_dataset = get_movie_dataset()
print(movie_dataset)

基于TF·IDF提取TOP-N关键词,构建电影画像

from gensim.models import TfidfModel

import pandas as pd
import numpy as np

from pprint import pprint

# ......

def create_movie_profile(movie_dataset):
    '''
    使用tfidf,分析提取topn关键词
    :param movie_dataset: 
    :return: 
    '''
    dataset = movie_dataset["tags"].values

    from gensim.corpora import Dictionary
    # 根据数据集建立词袋,并统计词频,将所有词放入一个词典,使用索引进行获取
    dct = Dictionary(dataset)
    # 根据将每条数据,返回对应的词索引和词频
    corpus = [dct.doc2bow(line) for line in dataset]
    # 训练TF-IDF模型,即计算TF-IDF值
    model = TfidfModel(corpus)

    movie_profile = {}
    for i, mid in enumerate(movie_dataset.index):
        # 根据每条数据返回,向量
        vector = model[corpus[i]]
        # 按照TF-IDF值得到top-n的关键词
        movie_tags = sorted(vector, key=lambda x: x[1], reverse=True)[:30]
        # 根据关键词提取对应的名称
        movie_profile[mid] = dict(map(lambda x:(dct[x[0]], x[1]), movie_tags))

    return movie_profile

movie_dataset = get_movie_dataset()
pprint(create_movie_profile(movie_dataset))

完善画像关键词

from gensim.models import TfidfModel

import pandas as pd
import numpy as np

from pprint import pprint


def create_movie_profile(movie_dataset):
    '''
    使用tfidf,分析提取topn关键词
    :param movie_dataset:
    :return:
    '''
    dataset = movie_dataset["tags"].values

    from gensim.corpora import Dictionary
    # 根据数据集建立词袋,并统计词频,将所有词放入一个词典,使用索引进行获取
    dct = Dictionary(dataset)
    # 根据将每条数据,返回对应的词索引和词频
    corpus = [dct.doc2bow(line) for line in dataset]
    # 训练TF-IDF模型,即计算TF-IDF值
    model = TfidfModel(corpus)

    _movie_profile = []
    for i, data in enumerate(movie_dataset.itertuples()):
        mid = data[0]
        title = data[1]
        genres = data[2]
        vector = model[corpus[i]]
        movie_tags = sorted(vector, key=lambda x: x[1], reverse=True)[:30]
        topN_tags_weights = dict(map(lambda x: (dct[x[0]], x[1]), movie_tags))
        # 将类别词的添加进去,并设置权重值为1.0
        for g in genres:
            topN_tags_weights[g] = 1.0
        topN_tags = [i[0] for i in topN_tags_weights.items()]
        _movie_profile.append((mid, title, topN_tags, topN_tags_weights))

    movie_profile = pd.DataFrame(_movie_profile, columns=["movieId", "title", "profile", "weights"])
    movie_profile.set_index("movieId", inplace=True)
    return movie_profile

movie_dataset = get_movie_dataset()
pprint(create_movie_profile(movie_dataset))

为了根据指定关键词迅速匹配到对应的电影,因此需要对物品画像的标签词,建立倒排索引

倒排索引介绍

通常数据存储数据,都是以物品的ID作为索引,去提取物品的其他信息数据

而倒排索引就是用物品的其他数据作为索引,去提取它们对应的物品的ID列表

'''
建立tag-物品的倒排索引
'''

def create_inverted_table(movie_profile):
    inverted_table = {}
    for mid, weights in movie_profile["weights"].iteritems():
        for tag, weight in weights.items():
            #到inverted_table dict 用tag作为Key去取值 如果取不到就返回[]
            _ = inverted_table.get(tag, [])
            _.append((mid, weight))
            inverted_table.setdefault(tag, _)
    return inverted_table

inverted_table = create_inverted_table(movie_profile)
pprint(inverted_table)

基于内容的电影推荐:用户画像

用户画像构建步骤:

  • 根据用户的评分历史,结合物品画像,将有观影记录的电影的画像标签作为初始标签反打到用户身上
  • 通过对用户观影标签的次数进行统计,计算用户的每个初始标签的权重值,排序后选取TOP-N作为用户最终的画像标签

用户画像建立

import pandas as pd
import numpy as np
from gensim.models import TfidfModel

from functools import reduce
import collections

from pprint import pprint



'''
user profile画像建立:
1. 提取用户观看列表
2. 根据观看列表和物品画像为用户匹配关键词,并统计词频
3. 根据词频排序,最多保留TOP-k个词,这里K设为100,作为用户的标签
'''

def create_user_profile():
    watch_record = pd.read_csv("datasets/ml-latest-small/ratings.csv", usecols=range(2), dtype={"userId":np.int32, "movieId": np.int32})

    watch_record = watch_record.groupby("userId").agg(list)
    # print(watch_record)

    movie_dataset = get_movie_dataset()
    movie_profile = create_movie_profile(movie_dataset)

    user_profile = {}
    for uid, mids in watch_record.itertuples():
        record_movie_prifole = movie_profile.loc[list(mids)]
        counter = collections.Counter(reduce(lambda x, y: list(x)+list(y), record_movie_prifole["profile"].values))
        # 兴趣词
        interest_words = counter.most_common(50)
        maxcount = interest_words[0][1]
        interest_words = [(w,round(c/maxcount, 4)) for w,c in interest_words]
        user_profile[uid] = interest_words

    return user_profile

user_profile = create_user_profile()
pprint(user_profile)

基于内容的电影推荐:为用户产生TOP-N推荐结果

user_profile = create_user_profile()

watch_record = pd.read_csv("datasets/ml-latest-small/ratings.csv", usecols=range(2),dtype={"userId": np.int32, "movieId": np.int32})

watch_record = watch_record.groupby("userId").agg(list)

for uid, interest_words in user_profile.items():
    result_table = {} # 电影id:[0.2,0.5,0.7]
    for interest_word, interest_weight in interest_words:
        related_movies = inverted_table[interest_word]
        for mid, related_weight in related_movies:
            _ = result_table.get(mid, [])
            _.append(interest_weight)    # 只考虑用户的兴趣程度
            # _.append(related_weight)    # 只考虑兴趣词与电影的关联程度
            # _.append(interest_weight*related_weight)    # 二者都考虑
            result_table.setdefault(mid, _)

    rs_result = map(lambda x: (x[0], sum(x[1])), result_table.items())
    rs_result = sorted(rs_result, key=lambda x:x[1], reverse=True)[:100]
    print(uid)
    pprint(rs_result)
    break
    
    # 历史数据  ==>  历史兴趣程度 ==>  历史推荐结果       离线推荐    离线计算
    # 在线推荐 ===>    娱乐(王思聪)   ===>   我 ==>  王思聪 100%  
    # 近线:最近1天、3天、7天           实时计算

基于内容的电影推荐:物品冷启动处理

利用Word2Vec可以计算电影所有标签词之间的关系程度,可用于计算电影之间的相似度

word2vec原理简介

word2vec是google在2013年开源的一个NLP(Natural Language Processing自然语言处理) 工具,它的特点是将所有的词向量化,这样词与词之间就可以定量的去度量他们之间的关系,挖掘词之间的联系。

one-hot vector VS. word vector
  • 用向量来表示词并不是word2vec的首创
  • 最早的词向量是很冗长的,它使用是词向量维度大小为整个词汇表的大小,对于每个具体的词汇表中的词,将对应的位置置为1。
  • 比如下面5个词组成词汇表,词"Queen"的序号为2, 那么它的词向量就是(0,1,0,0,0)同样的道理,词"Woman"的词向量就是(0,0,0,1,0)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hSD3U5H0-1678011026596)(./img/word2vec1.png)]

one hot vector的问题
  • 如果词汇表非常大,如达到万级别,这样每个词都用万维的向量来表示浪费内存。这样的向量除了一个位置是1,其余位置全部为0,表达效率低(稀疏),需要降低词向量的维度
  • 难以发现词之间的关系,以及难以捕捉句法(结构)和语义(意思)之间的关系
  • Dristributed representation可以解决One hot representation的问题,它的思路是通过训练,将每个词都映射到一个较短的词向量上来。所有的这些词向量就构成了向量空间,进而可以用普通的统计学的方法来研究词与词之间的关系。这个较短的词向量维度一般需要我们在训练时指定。
  • 比如下图我们将词汇表里的词用"Royalty(王位)",“Masculinity(男性气质)”, "Femininity(女性气质)"和"Age"4个维度来表示,King这个词对应的词向量可能是(0.99,0.99,0.05,0.7)。当然在实际情况中,我们并不一定能对词向量的每个维度做一个很好的解释。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GQoAl4Zn-1678011026598)(./img/word2vec2.png)]

  • 有了用Dristributed representation表示的较短的词向量,就可以较容易的分析词之间的关系,比如将词的维度降维到2维,用下图的词向量表示我们的词时,发现: K i n g ⃗ − M a n ⃗ + W o m a n ⃗ = Q u e e n ⃗ \vec{King} - \vec{Man} + \vec{Woman} = \vec{Queen} King Man +Woman =Queen

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RV6sbH5G-1678011026601)(./img/word2vec3.png)]

什么是word vector(词向量)

每个单词被表征为多维的浮点数,每一维的浮点数的数值大小表示了它与另一个单词之间的“距离”,表征的结果就是语义相近的词被映射到相近的集合空间上,好处是这样单词之间就是可以计算的:

animal pet
dog -0.4 0.02
lion 0.2 0.35

animal那一列表示的就是左边的词与animal这个概念的”距离“

Word2Vec
两个重要模型:CBOW和Skip-Gram
  • 介绍:CBOW把一个词从词窗剔除。在CBOW下给定n词围绕着词w,word2vec预测一个句子中其中一个缺漏的词c,即以概率 p ( c ∣ w ) p(c|w) p(cw)来表示。相反地,Skip-gram给定词窗中的文本,预测当前的词 p ( w ∣ c ) p(w|c) p(wc)

  • 原理:拥有差不多上下文的两个单词的意思往往是相近的

  • Continuous Bag-of-Words(CBOW) 连续词袋向量

    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8LxXM0z7-1678011026604)(./img/CBOW.png)]

    • 功能:通过上下文预测当前词出现的概率

    • 原理分析

      假设文本如下:“the florid prose of the nineteenth century.

      想象有个滑动窗口,中间的词是关键词,两边为相等长度的文本来帮助分析。文本的长度为7,就得到了7个one-hot向量,作为神经网络的输入向量,训练目标是:最大化在给定前后文本情况下输出正确关键词的概率,比如给定(“prose”,“of”,“nineteenth”,“century”)的情况下,要最大化输出"the"的概率,用公式表示就是

      P(“the”|(“prose”,“of”,“nineteenth”,“century”))

    • 特性

      • hidden layer只是将权重求和,传递到下一层,是线性的
  • Continuous Skip-gram

    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nMWWit9n-1678011026606)(./img/skip-gram.png)]
    • 功能:根据当前词预测上下文
    • 原理分析
      • 和CBOW相反,则我们要求的概率就变为P(Context(w)|w)
  • **总结:**word2vec算法可以计算出每个词语的一个词向量,我们可以用它来表示该词的语义层面的含义

Word2Vec使用

from gensim.models import TfidfModel

import pandas as pd
import numpy as np


def get_movie_dataset():
    # 加载基于所有电影的标签
    # all-tags.csv来自ml-latest数据集中
    # 由于ml-latest-small中标签数据太多,因此借助其来扩充
    _tags = pd.read_csv("datasets/ml-latest-small/all-tags.csv", usecols=range(1, 3)).dropna()
    tags = _tags.groupby("movieId").agg(list)

    # 加载电影列表数据集
    movies = pd.read_csv("datasets/ml-latest-small/movies.csv", index_col="movieId")
    # 将类别词分开
    movies["genres"] = movies["genres"].apply(lambda x: x.split("|"))
    # 为每部电影匹配对应的标签数据,如果没有将会是NAN
    movies_index = set(movies.index) & set(tags.index)
    new_tags = tags.loc[list(movies_index)]
    ret = movies.join(new_tags)

    # 构建电影数据集,包含电影Id、电影名称、类别、标签四个字段
    # 如果电影没有标签数据,那么就替换为空列表
    movie_dataset = pd.DataFrame(
        map(
            lambda x: (x[0], x[1], x[2], x[2]+x[3]) if x[3] is not np.nan else (x[0], x[1], x[2], []), ret.itertuples())
        , columns=["movieId", "title", "genres","tags"]
    )

    movie_dataset.set_index("movieId", inplace=True)
    return movie_dataset


def create_movie_profile(movie_dataset):
    '''
    使用tfidf,分析提取topn关键词
    :param movie_dataset:
    :return:
    '''
    dataset = movie_dataset["tags"].values

    from gensim.corpora import Dictionary
    dct = Dictionary(dataset)
    corpus = [dct.doc2bow(line) for line in dataset]

    model = TfidfModel(corpus)

    _movie_profile = []
    for i, data in enumerate(movie_dataset.itertuples()):
        mid = data[0]
        title = data[1]
        genres = data[2]
        vector = model[corpus[i]]
        movie_tags = sorted(vector, key=lambda x: x[1], reverse=True)[:30]
        topN_tags_weights = dict(map(lambda x: (dct[x[0]], x[1]), movie_tags))
        # 将类别词的添加进去,并设置权重值为1.0
        for g in genres:
            topN_tags_weights[g] = 1.0
        topN_tags = [i[0] for i in topN_tags_weights.items()]
        _movie_profile.append((mid, title, topN_tags, topN_tags_weights))

    movie_profile = pd.DataFrame(_movie_profile, columns=["movieId", "title", "profile", "weights"])
    movie_profile.set_index("movieId", inplace=True)
    return movie_profile

movie_dataset = get_movie_dataset()
movie_profile = create_movie_profile(movie_dataset)

import gensim, logging

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

sentences = list(movie_profile["profile"].values)

model = gensim.models.Word2Vec(sentences, window=3, min_count=1, iter=20)

while True:
    words = input("words: ")  # action
    ret = model.wv.most_similar(positive=[words], topn=10)
    print(ret)
    

Doc2Vec是建立在Word2Vec上的,用于直接计算以文档为单位的文档向量,这里我们将一部电影的所有标签词,作为整个文档,这样可以计算出每部电影的向量,通过计算向量之间的距离,来判断用于计算电影之间的相似程度。

这样可以解决物品冷启动问题

Doc2Vec使用

from gensim.models import TfidfModel

import pandas as pd
import numpy as np

from pprint import pprint


def get_movie_dataset():
    # 加载基于所有电影的标签
    # all-tags.csv来自ml-latest数据集中
    # 由于ml-latest-small中标签数据太多,因此借助其来扩充
    _tags = pd.read_csv("datasets/ml-latest-small/all-tags.csv", usecols=range(1, 3)).dropna()
    tags = _tags.groupby("movieId").agg(list)

    # 加载电影列表数据集
    movies = pd.read_csv("datasets/ml-latest-small/movies.csv", index_col="movieId")
    # 将类别词分开
    movies["genres"] = movies["genres"].apply(lambda x: x.split("|"))
    # 为每部电影匹配对应的标签数据,如果没有将会是NAN
    movies_index = set(movies.index) & set(tags.index)
    new_tags = tags.loc[list(movies_index)]
    ret = movies.join(new_tags)

    # 构建电影数据集,包含电影Id、电影名称、类别、标签四个字段
    # 如果电影没有标签数据,那么就替换为空列表
    movie_dataset = pd.DataFrame(
        map(
            lambda x: (x[0], x[1], x[2], x[2]+x[3]) if x[3] is not np.nan else (x[0], x[1], x[2], []), ret.itertuples())
        , columns=["movieId", "title", "genres","tags"]
    )

    movie_dataset.set_index("movieId", inplace=True)
    return movie_dataset


def create_movie_profile(movie_dataset):
    '''
    使用tfidf,分析提取topn关键词
    :param movie_dataset:
    :return:
    '''
    dataset = movie_dataset["tags"].values

    from gensim.corpora import Dictionary
    dct = Dictionary(dataset)
    corpus = [dct.doc2bow(line) for line in dataset]

    model = TfidfModel(corpus)

    _movie_profile = []
    for i, data in enumerate(movie_dataset.itertuples()):
        mid = data[0]
        title = data[1]
        genres = data[2]
        vector = model[corpus[i]]
        movie_tags = sorted(vector, key=lambda x: x[1], reverse=True)[:30]
        topN_tags_weights = dict(map(lambda x: (dct[x[0]], x[1]), movie_tags))
        # 将类别词的添加进去,并设置权重值为1.0
        for g in genres:
            topN_tags_weights[g] = 1.0
        topN_tags = [i[0] for i in topN_tags_weights.items()]
        _movie_profile.append((mid, title, topN_tags, topN_tags_weights))

    movie_profile = pd.DataFrame(_movie_profile, columns=["movieId", "title", "profile", "weights"])
    movie_profile.set_index("movieId", inplace=True)
    return movie_profile

movie_dataset = get_movie_dataset()
movie_profile = create_movie_profile(movie_dataset)

import gensim, logging
from gensim.models.doc2vec import Doc2Vec, TaggedDocument

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

documents = [TaggedDocument(words, [movie_id]) for movie_id, words in movie_profile["profile"].iteritems()]

# 训练模型并保存
model = Doc2Vec(documents, vector_size=100, window=3, min_count=1, workers=4, epochs=20)
from gensim.test.utils import get_tmpfile
fname = get_tmpfile("my_doc2vec_model")
model.save(fname)


words = movie_profile["profile"].loc[6]
print(words)
inferred_vector = model.infer_vector(words)
sims = model.docvecs.most_similar([inferred_vector], topn=10)
print(sims)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值