酒店推荐系统代码详细解读
AI大模型应用开发者 2025年08月07日 19:50 北京
这个酒店推荐系统使用了以下关键技术:
1. **文本预处理**:将原始文本转换为标准化、清洗后的格式
2. **特征提取**:使用TF-IDF将文本转换为数值特征向量
3. **相似度计算**:使用余弦相似度衡量酒店描述之间的相似程度
4. **推荐算法**:基于相似度为用户推荐最相似的酒店
这种基于内容的推荐系统不需要用户交互数据,只依赖于酒店描述的文本内容,适合冷启动场景或数据稀疏的情况。
import pandas as pd
from sklearn.metrics.pairwise import linear_kernel
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfVectorizer
import re
pd.options.display.max_columns = 30
import matplotlib.pyplot as plt
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
df = pd.read_csv('hotel_recommendation/Seattle_Hotels.csv', encoding="latin-1")
# 数据探索
print(df.head())
print('数据集中的酒店个数:', len(df))
# 创建英文停用词列表
ENGLISH_STOPWORDS = {
'i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your',
'yours', 'yourself', 'yourselves', 'he', 'him', 'his', 'himself', 'she', "she's", 'her', 'hers', 'herself', 'it',
"it's", 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', 'what', 'which', 'who', 'whom', 'this',
'that', "that'll", 'these', 'those', 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', 'have', 'has', 'had',
'having', 'do', 'does', 'did', 'doing', 'a', 'an', 'the', 'and', 'but', 'if', 'or', 'because', 'as', 'until', 'while',
'of', 'at', 'by', 'for', 'with', 'about', 'against', 'between', 'into', 'through', 'during', 'before', 'after', 'above',
'below', 'to', 'from', 'up', 'down', 'in', 'out', 'on', 'off', 'over', 'under', 'again', 'further', 'then', 'once',
'here', 'there', 'when', 'where', 'why', 'how', 'all', 'any', 'both', 'each', 'few', 'more', 'most', 'other', 'some',
'such', 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too', 'very', 's', 't', 'can', 'will', 'just', 'don',
"don't", 'should', "should've", 'now', 'd', 'll', 'm', 'o', 're', 've', 'y', 'ain', 'aren', "aren't", 'couldn',
"couldn't", 'didn', "didn't", 'doesn', "doesn't", 'hadn', "hadn't", 'hasn', "hasn't", 'haven', "haven't", 'isn',
"isn't", 'ma', 'mightn', "mightn't", 'mustn', "mustn't", 'needn', "needn't", 'shan', "shan't", 'shouldn', "shouldn't",
'wasn', "wasn't", 'weren', "weren't", 'won', "won't", 'wouldn', "wouldn't"
}
def print_description(index):
example = df[df.index == index][['desc', 'name']].values[0]
if len(example) > 0:
print(example[0])
print('Name:', example[1])
print('第10个酒店的描述:')
print_description(10)
# 得到酒店描述中n-gram特征中的TopK个
def get_top_n_words(corpus, n=1, k=None):
# 统计ngram词频矩阵,使用自定义停用词列表
vec = CountVectorizer(ngram_range=(n, n), stop_words=list(ENGLISH_STOPWORDS)).fit(corpus)
bag_of_words = vec.transform(corpus)
"""
print('feature names:')
print(vec.get_feature_names())
print('bag of words:')
print(bag_of_words.toarray())
"""
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(df['desc'], n=3, k=20)
#print(common_words)
df1 = pd.DataFrame(common_words, columns = ['desc' , 'count'])
df1.groupby('desc').sum()['count'].sort_values().plot(kind='barh', title='去掉停用词后,酒店描述中的Top20单词')
plt.show()
# 文本预处理
REPLACE_BY_SPACE_RE = re.compile('[/(){}\[\]\|@,;]')
BAD_SYMBOLS_RE = re.compile('[^0-9a-z #+_]')
# 使用自定义的英文停用词列表替代nltk的stopwords
STOPWORDS = ENGLISH_STOPWORDS
# 对文本进行清洗
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字段进行清理,apply针对某列
df['desc_clean'] = df['desc'].apply(clean_text)
#print(df['desc_clean'])
# 建模
df.set_index('name', inplace = True)
# 使用TF-IDF提取文本特征,使用自定义停用词列表
tf = TfidfVectorizer(analyzer='word', ngram_range=(1, 3), min_df=0.01, stop_words=list(ENGLISH_STOPWORDS))
# 针对desc_clean提取tfidf
tfidf_matrix = tf.fit_transform(df['desc_clean'])
print('TFIDF feature names:')
#print(tf.get_feature_names_out())
print(len(tf.get_feature_names_out()))
#print('tfidf_matrix:')
#print(tfidf_matrix)
#print(tfidf_matrix.shape)
# 计算酒店之间的余弦相似度(线性核函数)
cosine_similarities = linear_kernel(tfidf_matrix, tfidf_matrix)
#print(cosine_similarities)
print(cosine_similarities.shape)
indices = pd.Series(df.index) #df.index是酒店名称
# 基于相似度矩阵和指定的酒店name,推荐TOP10酒店
def recommendations(name, cosine_similarities = cosine_similarities):
recommended_hotels = []
# 找到想要查询酒店名称的idx
idx = indices[indices == name].index[0]
print('idx=', idx)
# 对于idx酒店的余弦相似度向量按照从大到小进行排序
score_series = pd.Series(cosine_similarities[idx]).sort_values(ascending = False)
# 取相似度最大的前10个(除了自己以外)
top_10_indexes = list(score_series.iloc[1:11].index)
# 放到推荐列表中
for i in top_10_indexes:
recommended_hotels.append(list(df.index)[i])
return recommended_hotels
print(recommendations('Hilton Seattle Airport & Conference Center'))
print(recommendations('The Bacon Mansion Bed and Breakfast'))
#print(result)
# 酒店推荐系统代码详细解读
这个代码实现了一个基于文本相似度的酒店推荐系统,使用了自然语言处理和机器学习技术。下面是对代码的详细解读:
## 导入必要的库
```python
import pandas as pd
```
这行导入了pandas库,并将其简写为pd。pandas是Python中用于数据分析和处理的核心库,提供了DataFrame等数据结构,可以高效地处理和分析结构化数据。
```python
from sklearn.metrics.pairwise import linear_kernel
```
从scikit-learn库中导入linear_kernel函数,用于计算向量之间的线性核(本质上是计算余弦相似度),这在后面用于计算酒店描述之间的相似度。
```python
from sklearn.feature_extraction.text import CountVectorizer
```
导入CountVectorizer类,用于将文本数据转换为词频矩阵,即计算文本中每个词出现的次数。
```python
from sklearn.feature_extraction.text import TfidfVectorizer
```
导入TfidfVectorizer类,用于将文本转换为TF-IDF特征矩阵,这是一种常用的文本特征提取方法,可以反映词语在文档中的重要性。
```python
import re
```
导入正则表达式库,用于文本清洗和处理。
```python
pd.options.display.max_columns = 30
```
设置pandas显示的最大列数为30,这样可以在打印DataFrame时显示更多的列。
```python
import matplotlib.pyplot as plt
```
导入matplotlib的pyplot模块,用于数据可视化。
```python
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
```
设置matplotlib的字体为SimHei,以支持显示中文字符,避免中文显示为乱码。
## 数据加载与探索
```python
df = pd.read_csv('hotel_recommendation/Seattle_Hotels.csv', encoding="latin-1")
```
从指定路径读取CSV文件到DataFrame中,使用latin-1编码(这对于处理包含特殊字符的文件很有用)。
```python
# 数据探索
print(df.head())
```
打印DataFrame的前几行数据,用于初步了解数据结构。
```python
print('数据集中的酒店个数:', len(df))
```
打印数据集中酒店的总数量。
## 停用词处理
```python
# 创建英文停用词列表
ENGLISH_STOPWORDS = {
'i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', 'you', "you're", "you've", "you'll", "you'd", 'your',
# ... 更多停用词 ...
}
```
创建一个包含英文常见停用词的集合。停用词是在文本分析中通常被过滤掉的常见词(如"the"、"a"、"an"等),因为它们出现频率高但不携带重要信息。
## 酒店描述查看函数
```python
def print_description(index):
example = df[df.index == index][['desc', 'name']].values[0]
if len(example) > 0:
print(example[0])
print('Name:', example[1])
```
定义一个函数,用于打印指定索引的酒店描述和名称。这个函数首先根据索引筛选出对应的酒店记录,然后打印其描述和名称。
```python
print('第10个酒店的描述:')
print_description(10)
```
打印索引为10的酒店的描述信息。
## 文本特征提取
```python
# 得到酒店描述中n-gram特征中的TopK个
def get_top_n_words(corpus, n=1, k=None):
# 统计ngram词频矩阵,使用自定义停用词列表
vec = CountVectorizer(ngram_range=(n, n), stop_words=list(ENGLISH_STOPWORDS)).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]
```
定义一个函数,用于获取文本语料库中出现频率最高的n-gram特征。n-gram是指连续的n个词组成的序列。这个函数:
1. 使用CountVectorizer将文本转换为n-gram词频矩阵
2. 计算每个n-gram在所有文档中的总出现次数
3. 创建(词, 频率)对的列表
4. 按频率降序排序
5. 返回前k个最常见的n-gram
```python
common_words = get_top_n_words(df['desc'], n=3, k=20)
```
获取酒店描述中出现频率最高的20个三元词组(trigrams)。
```python
df1 = pd.DataFrame(common_words, columns = ['desc' , 'count'])
df1.groupby('desc').sum()['count'].sort_values().plot(kind='barh', title='去掉停用词后,酒店描述中的Top20单词')
plt.show()
```
将常见词及其频率转换为DataFrame,然后创建一个水平条形图来可视化Top20的词频分布。
## 文本预处理
```python
# 文本预处理
REPLACE_BY_SPACE_RE = re.compile('[/(){}\[\]\|@,;]')
BAD_SYMBOLS_RE = re.compile('[^0-9a-z #+_]')
```
定义两个正则表达式模式:
1. REPLACE_BY_SPACE_RE:匹配需要被空格替换的特殊符号
2. BAD_SYMBOLS_RE:匹配需要被删除的不需要的符号
```python
# 使用自定义的英文停用词列表替代nltk的stopwords
STOPWORDS = ENGLISH_STOPWORDS
```
将之前定义的停用词集合赋值给STOPWORDS变量。
```python
# 对文本进行清洗
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
```
定义一个文本清洗函数,包括以下步骤:
1. 将文本转换为小写
2. 用空格替换特定的标点符号
3. 删除不需要的符号
4. 移除停用词
```python
# 对desc字段进行清理,apply针对某列
df['desc_clean'] = df['desc'].apply(clean_text)
```
对DataFrame中的'desc'列应用clean_text函数,创建一个新的清洗后的描述列'desc_clean'。
## 建立推荐模型
```python
# 建模
df.set_index('name', inplace = True)
```
将DataFrame的索引设置为酒店名称,这样可以直接通过酒店名称来访问数据。
```python
# 使用TF-IDF提取文本特征,使用自定义停用词列表
tf = TfidfVectorizer(analyzer='word', ngram_range=(1, 3), min_df=0.01, stop_words=list(ENGLISH_STOPWORDS))
```
创建TF-IDF向量化器,用于将文本转换为TF-IDF特征:
- analyzer='word':以单词为单位进行分析
- ngram_range=(1, 3):考虑1-gram到3-gram的特征
- min_df=0.01:忽略在少于1%文档中出现的词
- stop_words=list(ENGLISH_STOPWORDS):使用自定义停用词列表
```python
# 针对desc_clean提取tfidf
tfidf_matrix = tf.fit_transform(df['desc_clean'])
```
将清洗后的酒店描述转换为TF-IDF特征矩阵。
```python
print('TFIDF feature names:')
print(len(tf.get_feature_names_out()))
```
打印TF-IDF特征的数量,即提取出的独特词汇或n-gram的数量。
```python
# 计算酒店之间的余弦相似度(线性核函数)
cosine_similarities = linear_kernel(tfidf_matrix, tfidf_matrix)
```
使用线性核函数计算TF-IDF矩阵中每对酒店描述之间的余弦相似度,得到一个相似度矩阵。
```python
print(cosine_similarities.shape)
```
打印相似度矩阵的形状,应该是(酒店数量, 酒店数量)。
```python
indices = pd.Series(df.index) #df.index是酒店名称
```
创建一个Series,包含所有酒店名称,用于后续通过名称查找索引。
## 推荐函数
```python
# 基于相似度矩阵和指定的酒店name,推荐TOP10酒店
def recommendations(name, cosine_similarities = cosine_similarities):
recommended_hotels = []
# 找到想要查询酒店名称的idx
idx = indices[indices == name].index[0]
print('idx=', idx)
# 对于idx酒店的余弦相似度向量按照从大到小进行排序
score_series = pd.Series(cosine_similarities[idx]).sort_values(ascending = False)
# 取相似度最大的前10个(除了自己以外)
top_10_indexes = list(score_series.iloc[1:11].index)
# 放到推荐列表中
for i in top_10_indexes:
recommended_hotels.append(list(df.index)[i])
return recommended_hotels
```
定义一个推荐函数,基于酒店名称和相似度矩阵推荐最相似的10个酒店:
1. 通过酒店名称找到对应的索引
2. 获取该酒店与所有其他酒店的相似度向量
3. 按相似度降序排序
4. 选取相似度最高的10个酒店(排除自身)
5. 返回这10个酒店的名称列表
```python
print(recommendations('Hilton Seattle Airport & Conference Center'))
print(recommendations('The Bacon Mansion Bed and Breakfast'))
```
分别打印对"Hilton Seattle Airport & Conference Center"和"The Bacon Mansion Bed and Breakfast"这两个酒店的推荐结果。
## 总结
这个酒店推荐系统使用了以下关键技术:
1. **文本预处理**:将原始文本转换为标准化、清洗后的格式
2. **特征提取**:使用TF-IDF将文本转换为数值特征向量
3. **相似度计算**:使用余弦相似度衡量酒店描述之间的相似程度
4. **推荐算法**:基于相似度为用户推荐最相似的酒店
这种基于内容的推荐系统不需要用户交互数据,只依赖于酒店描述的文本内容,适合冷启动场景或