基于python构建搜索引擎系列——(五)推荐阅读
虽然主要的检索功能实现了,但是我们还需要一个“推荐阅读”的功能。当用户浏览某条具体新闻时,我们在页面底端给出5条和该新闻相关的新闻,也就是一个最简单的推荐系统。
推荐模块的思路是度量两两新闻之间的相似度,取相似度最高的前5篇新闻作为推荐阅读的新闻。
我们前面讲过,一篇文档可以用一个向量表示,向量中的每个值是不同词项t在该文档d中的词频tf。但是一篇较短的文档(如新闻)的关键词并不多,所以我们可以提取每篇新闻的关键词,用这些关键词的tfidf值构成文档的向量表示,这样能够大大减少相似度计算量,同时保持较好的推荐效果。
jieba分词组件自带关键词提取功能 ,并能返回关键词的tfidf值。所以对每篇新闻,我们先提取tfidf得分最高的前25个关键词,用这25个关键词的tfidf值作为文档的向量表示。由此能够得到一个1000*m的文档词项矩阵M,矩阵每行表示一个文档,每列表示一个词项,m为1000个文档的所有互异的关键词(大概10000个)。矩阵M当然也是稀疏矩阵。
得到文档词项矩阵M之后,我们利用 sklearn的pairwise_distances函数 计算M中行向量之间的cosine相似度,对每个文档,得到与其最相似的前5篇新闻id,并把结果写入数据库。
推荐阅读模块的代码如下:
```python from os import listdir import xml.etree.ElementTree as ET import jieba import jieba.analyse import sqlite3 import configparser from datetime import * import math
import pandas as pd import numpy as np
from sklearn.metrics import pairwise_distances
class RecommendationModule: stop_words = set() k_nearest = []
config_path = ''
config_encoding = ''
doc_dir_path = ''
doc_encoding = ''
stop_words_path = ''
stop_words_encoding = ''
idf_path = ''
db_path = ''
def __init__(self, config_path, config_encoding):
self.config_path = config_path
self.config_encoding = config_encoding
config = configparser.ConfigParser()
config.read(config_path, config_encoding)
self.doc_dir_path = config['DEFAULT']['doc_dir_path']
self.doc_encoding = config['DEFAULT']['doc_encoding']
self.stop_words_path = config['DEFAULT']['stop_words_path']
self.stop_words_encoding = config['DEFAULT']['stop_words_encoding']
self.idf_path = config['DEFAULT']['idf_path']
self.db_path = config['DEFAULT']['db_path']
f = open(self.stop_words_path, encoding = self.stop_words_encoding)
words = f.read()
self.stop_words = set(words.split('\n'))
def write_k_nearest_matrix_to_db(self):
conn = sqlite3.connect(self.db_path)
c = conn.cursor()
c.execute('''DROP TABLE IF EXISTS knearest''')
c.execute('''CREATE TABLE knearest
(id INTEGER PRIMARY KEY, first INTEGER, second INTEGER,
third INTEGER, fourth INTEGER, fifth INTEGER)''')
for docid, doclist in self.k_nearest:
c.execute("INSERT INTO knearest VALUES (?, ?, ?, ?, ?, ?)", tuple([docid] + doclist))
conn.commit()
conn.close()
def is_number(self, s):
try:
float(s)
return True
except ValueError:
return False
def construct_dt_matrix(self, files, topK = 200):
jieba.analyse.set_stop_words(self.stop_words_path)
jieba.analyse.set_idf_path(self.idf_path)
M = len(files)
N = 1
terms = {}
dt = []
for i in files:
root = ET.parse(self.doc_dir_path + i).getroot()
title = root.find('title').text
body = root.find('body').text
docid = int(root.find('id').text)
tags = jieba.analyse.extract_tags(title + '。' + body, topK=topK, withWeight=True)
#tags = jieba.analyse.extract_tags(title, topK=topK, withWeight=True)
cleaned_dict = {}
for word, tfidf in tags:
word = word.strip().lower()
if word == '' or self.is_number(word):
continue
cleaned_dict[word] = tfidf
if word not in terms:
terms[word] = N
N += 1
dt.append([docid, cleaned_dict])
dt_matrix = [[0 for i in range(N)] for j in range(M)]
i =0
for docid, t_tfidf in dt:
dt_matrix[i][0] = docid
for term, tfidf in t_tfidf.items():
dt_matrix[i][terms[term]] = tfidf
i += 1
dt_matrix = pd.DataFrame(dt_matrix)
dt_matrix.index = dt_matrix[0]
print('dt_matrix shape:(%d %d)'%(dt_matrix.shape))
return dt_matrix
def construct_k_nearest_matrix(self, dt_matrix, k):
tmp = np.array(1 - pairwise_distances(dt_matrix[dt_matrix.columns[1:]], metric = "cosine"))
similarity_matrix = pd.DataFrame(tmp, index = dt_matrix.index.tolist(), columns = dt_matrix.index.tolist())
for i in similarity_matrix.index:
tmp = [int(i),[]]
j = 0
while j < k:
max_col = similarity_matrix.loc[i].idxmax(axis = 1)
similarity_matrix.loc[i][max_col] = -1
if max_col != i:
tmp[1].append(int(max_col)) #max column name
j += 1
self.k_nearest.append(tmp)
def gen_idf_file(self):
files = listdir(self.doc_dir_path)
n = float(len(files))
idf = {}
for i in files:
root = ET.parse(self.doc_dir_path + i).getroot()
title = root.find('title').text
body = root.find('body').text
seg_list = jieba.lcut(title + '。' + body, cut_all=False)
seg_list = set(seg_list) - self.stop_words
for word in seg_list:
word = word.strip().lower()
if word == '' or self.is_number(word):
continue
if word not in idf:
idf[word] = 1
else:
idf[word] = idf[word] + 1
idf_file = open(self.idf_path, 'w', encoding = 'utf-8')
for word, df in idf.items():
idf_file.write('%s %.9f\n'%(word, math.log(n / df)))
idf_file.close()
def find_k_nearest(self, k, topK):
self.gen_idf_file()
files = listdir(self.doc_dir_path)
dt_matrix = self.construct_dt_matrix(files, topK)
self.construct_k_nearest_matrix(dt_matrix, k)
self.write_k_nearest_matrix_to_db()
if name == " main ": print('-----start time: %s-----'%(datetime.today())) rm = RecommendationModule('../config.ini', 'utf-8') rm.find_k_nearest(5, 25) print('-----finish time: %s-----'%(datetime.today())) ```
这个模块的代码量最多,主要原因是需要构建文档词项矩阵,并且计算k邻居矩阵。矩阵数据结构的设计需要特别注意,否则会严重影响系统的效率。我刚开始把任务都扔给了pandas.DataFrame,后来发现当两个文档向量合并时,需要join连接操作,当数据量很大时,非常耗时,所以改成了先用python原始的list存储,最后一次性构造一个完整的pandas.DataFrame,速度比之前快了不少。
本文转载自:http://bitjoy.net/2016/01/09/introduction-to-building-a-search-engine-5
参考文献
- 面向特定网页的Web爬虫的设计与实现(吉林大学·马慧)
- 基于领域的网络爬虫技术的研究与实现(武汉理工大学·谭龙远)
- 基于Java平台的网络资源搜索系统的设计与实现(电子科技大学·李梦雅)
- 基于Web的网络搜索技术研究(西北工业大学·郭晨娟)
- 基于MapReduce的分布式搜索引擎研究与实现(太原理工大学·张超)
- 个性化垂直搜索引擎关键技术研究(山东科技大学·潘守慧)
- 基于协同过滤的推荐算法比较研究(重庆大学·高寒)
- 分布式网络爬虫在农产品搜索系统中的应用与研究(南昌大学·袁龙涛)
- 基于Lucene的搜索引擎的研究与实现(大连理工大学·宏朴)
- 个性化资讯推荐系统的设计与实现(山东大学·仵贇)
- 主题网络爬虫的研究和实现(武汉理工大学·林捷)
- 基于元搜索的Web信息搜索技术研究(吉林大学·张春磊)
- 基于爬虫的小企业搜索系统的设计与实现(大连理工大学·范能科)
- 基于Python的非结构化数据检索系统的设计与实现(南京邮电大学·董海兰)
- 基于偏好的教育推荐系统的业务逻辑设计(中南大学·)
本文内容包括但不限于文字、数据、图表及超链接等)均来源于该信息及资料的相关主题。发布者:毕设项目助手 ,原文地址:https://m.bishedaima.com/yuanma/35586.html