在推荐系统中,有一个环节是基于物品的推荐。它是基于物品的相似度,即物品的静态属性,如物品内容描述、物品特征向量等属性。这次我们会用到TFIDF和相似度计算的方法,对用户进行酒店推荐。本次使用的酒店数据会放到github中,需要自取:https://github.com/Vihagle/AI/tree/main/data.
下面的代码中,会提到余弦相似度,那我们也顺便提一嘴:
所谓余弦相似度,就是通过测量个向量的夹角的余弦值来度量它们之间的相似度。判断两个向量大致方向是否相同,
- 方向相同时,余弦相似度为1;
- 两个向量夹角为90°时,余弦相似度的值为0;
- 方向完全相反时,余弦相似度的值为-1
- 两个向量之间夹角的余弦值为[-1,1]
- 给定属性向量A和B,A和B之间的夹角θ余弦值可以通过点积和向量长度计算得出:
s i m i l a r i t y = c o s ( θ ) = A ⋅ B ∣ A ∣ ∣ B ∣ = ∑ i = 1 n A i ∗ B i ∑ i = 1 n ( A i ) 2 ∗ ∑ i = 1 n ( B i ) 2 similarity = cos(\theta) = \frac{A\cdot B}{|A||B|} = \frac{\sum^n_{i=1}A_i*B_i}{\sqrt{\sum^n_{i=1}(A_i)^2}*\sqrt{\sum^n_{i=1}(B_i)^2}} similarity=cos(θ)=∣A∣∣B∣A⋅B=∑i=1n(Ai)2∗∑i=1n(Bi)2∑i=1nAi∗Bi
除此之外,再了解一下什么是N-Gram(N元语法)
- 基于一个假设:第n个词的出现与前n-1个词相关,而与其他词无关
- N=1时为unigram,N=2为bigram,N=3为trigram
- N-Gram指的是给定一个文本,其中的N个item的序列
- 比如文本:ABCDE,对应的Bi-Gram为AB,BC,CD,DE
- 当一阶特征不够用时,可以用N-Gram作为新的特征。比如处理文本特征时,一个关键词是一个,但有些情况不太够用,需要提取更多的特征,需要提取更多特征,采用N-Gram=》可以理解是相邻两个关键词的特征组合。
代码展示:
import numpy as np
import pandas as pd
from nltk.corpus import stopwords
from sklearn.metrics.pairwise import linear_kernel
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.decomposition import LatentDirichletAllocation
import re
import random
#加载数据集
data = pd.read_table('./Seattle_Hotels.txt',encoding="latin-1",sep = ',')
data
#得到酒店描述中n-gram特征中Top10个
def get_top_n_words(corpus,n=1,k=10):
#统计ngram词频矩阵
vec = CountVectorizer(ngram_range=(n,n),stop_words='english').fit(corpus)
bag_of_words = vec.transform(corpus)
sum_words = bag_of_words.sum(axis=0)
words_freq = [(word, sum_words[0, idx]) for word, idx in vec.vocabulary_.items()]
#按照词频从大到小排序
words_freq = sorted(words_freq,key = lambda x:x[1],reverse = True)
return words_freq[:k]
common_words = get_top_n_words(data['desc'], 1, 10)
#对高频词进行可视化展示
import matplotlib.pyplot as plt
plt.figure(figsize = (10,8))
plt.barh([i[0] for i in common_words[::-1]],[i[1] for i in common_words[::-1]])
#文本预处理
REPLACE_BY_SPACE_RE = re.compile('[/(){}\|@,;]')
BAD_SYMBOLS_RE = re.compile('[^0-9a-z #+_]')
#加载停用词
STOPWORDS = set(stopwords.words('english'))
#对酒店描述进行文本清洗
def clean_text(text):
#全部文本转换成小写
text = text.lower()
#用空格替代标点符号
text = REPLACE_BY_SPACE_RE.sub(' ',text)
#移除BAD_SYMBOLS_RE
text = BAD_SYMBOLS_RE.sub(' ',text)
#去除停用词
text = ' '.join(word for word in text.split() if word not in STOPWORDS)
return text
#对'desc'字段进行文本清洗
data['clean_desc'] = data['desc'].apply(clean_text)
data
#将酒店名字作为索引
data.set_index('name',inplace = True)
#利用TF-IDF提取文本特征,min_df代表最小词频的百分比
tf = TfidfVectorizer(analyzer='word',ngram_range=(1,3),min_df = 0.01,stop_words = 'english')
#获取TF-IDF结果
res = tf.fit_transform(data['clean_desc'])
#获取TF-IDF矩阵
res_matrix = res.toarray()
#获取TF-IDF词名称
res_name = tf.get_feature_names()
#查看矩阵大小
res_matrix.shape #(152, 3245)
#计算酒店之间的相似度(基于余弦相似度)
cosine_similarities = linear_kernel(res,res)
#打印相似度矩阵
sim_hotel = pd.DataFrame(cosine_similarities,columns = data.index.tolist(),index = data.index.tolist())
sim_hotel
#基于相似度矩阵和指定酒店名称,推荐TOP10酒店
def recommandation(metrix,hotel):
rec_index = np.argsort(-metrix[hotel])[1:11] #这里从第二个索引开始取的原因是,相似度最高的肯定是指定酒店自身
rec = []
for index,i in enumerate(rec_index):
print(f'酒店推荐排行第{index+1}名:{metrix.index[i]}')
#打印推荐结果
recommandation(sim_hotel,'Hilton Seattle Airport & Conference Center')