基于酒店文本描述的相似酒店推荐系统:从TF-IDF到余弦相似度实战

一、项目背景

在旅游和酒店预订场景中,用户常常希望找到与某家心仪酒店风格、位置、设施相似的替代选择。传统方法依赖标签分类或协同过滤,但当缺乏用户行为数据时,如何仅利用酒店的描述文本进行相似度计算就成为了一个有趣且实用的 NLP 任务。

本文将带你完成一个完整的基于文本内容的相似酒店推荐系统。我们将使用西雅图地区酒店的公开数据集,通过 TF-IDF 向量化酒店描述文本,计算酒店之间的余弦相似度,最终实现"输入一家酒店,推荐 Top-10 最相似的酒店"。

二、项目环境与依赖

2.1 所需库

python 复制代码
import pandas as pd
import numpy as np
import re
import nltk
from nltk.corpus import stopwords
from sklearn.feature_extraction.text import CountVectorizer, TfidfVectorizer
from sklearn.metrics.pairwise import linear_kernel
import cufflinks as cf
from plotly.offline import iplot

cf.go_offline()

2.2 下载停用词

python 复制代码
nltk.download('stopwords')
stopwords_set = set(stopwords.words('english'))

2.3 数据集说明

数据文件为 Seattle_Hotels.csv,包含以下字段:

  • name:酒店名称

  • address:酒店地址

  • desc:酒店描述文本(核心用于推荐)

三、数据加载与初步探查

python 复制代码
df = pd.read_csv('Seattle_Hotels.csv', encoding='latin-1')
print(df.shape)   # 查看数据量
df.head()

样本输出示例

name address desc
Hilton Garden Seattle Downtown 1821 Boren Avenue... Located on the southern tip of Lake Union...
Sheraton Grand Seattle 1400 6th Avenue... Located in the city's vibrant core...

3.1 描述文本的统计信息

添加单词计数列,了解描述文本的长度分布:

python 复制代码
df['word_count'] = df['desc'].apply(lambda x: len(str(x).split()))

使用 plotly + cufflinks 绘制单词数直方图:

python 复制代码
df['word_count'].iplot(kind='hist', bins=50, title='Description Word Count Distribution')

从直方图可以看出,大部分酒店描述集中在 100~250 个单词之间,非常适合进行文本向量化。

四、文本分析与特征工程

4.1 词频统计(未去停用词)

使用 CountVectorizer 统计所有描述中单词出现的频率:

python 复制代码
vec = CountVectorizer().fit(df['desc'])
bow = vec.transform(df['desc'])
sum_words = bow.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)
words_freq[:20]

结果展示(Top 20 单词):

  • the (1258), and (1062), of (536), seattle (533), to (471), in (449) ...

可视化:

python 复制代码
df_top = pd.DataFrame(words_freq[:20], columns=['word', 'count'])
df_top.iplot(kind='barh', x='count', y='word', title='Top 20 Words (Before Stopwords)')

4.2 去停用词后的词频

python 复制代码
vec_clean = CountVectorizer(stop_words='english').fit(df['desc'])
bow_clean = vec_clean.transform(df['desc'])
# 同样提取 top 20

结果hotel, seattle, downtown, free, located, rooms, stay, pike, market 等成为高频关键词。

这表明酒店描述主要围绕地理位置、价格(free breakfast)、房型等用户关注点。

4.3 加入 n-gram 特征

使用 ngram_range=(1,3) 捕获短语信息(如 "pike place market"):

python 复制代码
vec_ngram = CountVectorizer(stop_words='english', ngram_range=(1,3)).fit(df['desc'])

可视化后可见 "pike place market" 出现 85 次,说明该景点是西雅图酒店描述的核心卖点之一。

五、文本预处理函数

为了使 TF-IDF 效果更好,我们编写一个简单的清洗函数:

python 复制代码
def clean_text(text):
    # 转小写
    text = text.lower()
    # 只保留字母、数字、空格、#、+、_
    text = re.sub(r'[^0-9a-z #+_]', '', text)
    # 去除停用词
    words = [word for word in text.split() if word not in stopwords_set]
    return ' '.join(words)

df['desc_clean'] = df['desc'].apply(clean_text)

注意:原 notebook 中 sub_replacestopwords 变量未正确导入,这里已修正。

六、TF-IDF 向量化与相似度计算

6.1 建立 TF-IDF 矩阵

python 复制代码
tfidf = TfidfVectorizer(analyzer='word', ngram_range=(1, 3), stop_words='english')
tfidf_matrix = tfidf.fit_transform(df['desc_clean'])
print(tfidf_matrix.shape)  # (样本数, 特征数)

6.2 计算余弦相似度矩阵

python 复制代码
cosine_sim = linear_kernel(tfidf_matrix, tfidf_matrix)

linear_kernel 计算两个矩阵的内积,对于已经归一化的 TF-IDF 向量等价于余弦相似度。

6.3 构建酒店索引映射

python 复制代码
indices = pd.Series(df.index)   # 酒店名称列表

七、推荐函数实现

python 复制代码
def recommend_hotels(name, cosine_sim, top_n=10):
    # 获取目标酒店的索引
    idx = indices[indices == name].index[0]
    # 获取所有酒店与目标酒店的相似度得分
    sim_scores = list(enumerate(cosine_sim[idx]))
    # 按相似度降序排序
    sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
    # 取前 top_n 个(排除自身)
    sim_scores = sim_scores[1:top_n+1]
    # 返回酒店名称列表
    hotel_indices = [i[0] for i in sim_scores]
    return list(df.index[hotel_indices])

测试示例

python 复制代码
recommend_hotels('Hilton Garden Seattle Downtown', cosine_sim)

输出(示例):

['Hilton Garden Seattle Downtown', # 自身通常被排除

'Sheraton Grand Seattle',

'Crowne Plaza Seattle Downtown',

'The Westin Seattle',

...]

可以看到推荐结果都是位于西雅图市中心的商务型酒店,说明文本相似度成功捕捉了"地理位置 + 酒店类型"的信息。

八、结果分析与优化方向

8.1 当前方法优点

  • 无需任何标签或用户行为数据,仅依赖文本内容。

  • TF-IDF 能突出酒店独特的关键词(如景点名、品牌名)。

  • 计算简单,可解释性强。

8.2 可能的改进

  1. 词干化/词形还原 :使用 nltk.stem 将不同词形归一化(如 "located" → "locat")。

  2. 加权调整:对某些重要特征(如酒店品牌、价格区间)手工提权。

  3. 混合推荐:结合地理坐标(经纬度)计算距离相似度,与文本相似度加权融合。

  4. 使用 Sentence-BERT:将句子嵌入为语义向量,效果往往优于 TF-IDF。


九、总结

本文完整实现了一个基于酒店描述文本的相似推荐系统,涵盖了:

  • 数据探索与可视化

  • 文本清洗与停用词过滤

  • TF-IDF 特征提取与余弦相似度计算

  • 推荐函数的封装与测试

这种基于内容的推荐方法可迁移到任何具有文本描述的物品推荐场景(如电影、新闻、商品等)。项目代码清晰,适合初学者快速上手 NLP 推荐任务。

相关推荐
SHolmes18543 天前
TF-IDF为什么能找出文本里的重要词?
tf-idf
人工干智能1 个月前
科普:CountVectorizer、TF、TF-IDF,三者层层递进
python·tf-idf
琪伦的工具库2 个月前
本地文档批量统计词权
tf-idf
阿钱真强道2 个月前
37 Python 时序和文本:词袋模型 BoW 和 TF-IDF 到底怎么理解?
python·nlp·tf-idf·文本向量化·词袋模型·bow
Dway2 个月前
TF-IDF
tf-idf
我材不敲代码2 个月前
Python 实战——红楼梦文本分析全流程:从分卷处理到分词再到TF-IDF 提取核心关键词
人工智能·自然语言处理·tf-idf
光羽隹衡4 个月前
机器学习——TF-IDF实战(红楼梦数据处理)
python·tf-idf
囊中之锥.4 个月前
从分词到词云:基于 TF-IDF 的中文关键词提取实践
前端·tf-idf·easyui
光羽隹衡5 个月前
机器学习——自然语言处理之关键词提取任务(TF-IDF)
机器学习·自然语言处理·tf-idf