基于神经网络的embeddding来构建推荐系统
在之前的博客中,我主要介绍了embedding用于处理类别特征的应用,其实,在学术界和工业界上,embedding的应用还有很多,比如在推荐系统中的应用。本篇博客就介绍了如何利用embedding来构建一个图书的推荐系统。
本文主要译自《Building a Recommendation System Using Neural Network Embeddings》,完整详细的代码见官方GitHub。
目录
一、背景&数据集读取
1.1 神经网络嵌入(Neural Network Embeddings)
1.2 数据集:来自维基百科
1.3 数据集清洗
二、监督学习
2.1 定义机器学习的任务
2.2 关于训练集及测试集的划分
2.3 嵌入模型
2.4 生成训练示例
2.5 训练模型
三、构建推荐系统
四、嵌入可视化
五、总结
参考文献
一、背景&数据集读取
深度学习应用甚广,在诸多方面的表现,如图像分割、时序预测和自然语言处理,都优于其他机器学习方法。以前,你只能在学术论文或者大型商业公司中看到它的身影,但如今,我们已能利用自己的电脑进行深度学习计算。本文将利用深度学习和维基百科构建图书推荐系统。
该推荐系统基于假设:链接到相似的维基百科页面的书籍彼此相似。(注:必须要理解本文所用的数据集才能这句话的深层含义。稍后会讲解数据集的含义)
1.1 神经网络嵌入(Neural Network Embeddings)
嵌入(embedding),即用连续向量表示离散变量的方法。与独热编码不同的是,神经网络嵌入维度较低,并能令相似实体在嵌入空间中相邻。
神经网络嵌入的主要用途有三种:
在嵌入空间中找到最近邻。
作为有监督的机器学习模型的输入。
挖掘变量间的关系。
1.2 数据集:来自维基百科
与以往的数据科学项目一样,我们需要从数据集入手。点击此处,查看如何下载和处理维基百科上的每一篇文章,以及搜索书籍页面。在本文的数据集中,我们保存了图书标题、基本信息、图书页面上指向其它维基百科页面的链接(wikilinks)和外部网站的链接。为了创建一个推荐系统,我们仅仅使用图书标题和wikilinks。
(注:我们在这里仔细解释下wikilinks:所谓的wikilinks就是在图书的维基百科页面上的介绍该书的一些词组,比如说《战争与和平》,那么它的wikilinks可能就是列夫托尔斯泰,俄国,俄语等。因为这些词语在介绍《战争与和平》时肯定会出现,而有的读者可能对这些词语感兴趣,因此,在《战争与和平》的页面上会有跳往这些词语的链接,这就是所谓的wikilinks。
因此,该推荐系统基于假设:链接到相似的维基百科页面的书籍彼此相似。这句话的意义就很明显了,如果2本书籍的wikilinks都指向了列夫托尔斯泰,俄国,俄语等,那么这2本书可能就是很相似。比如说《战争与和平》《安娜·卡列尼娜》。)
这是一本书和它的wikilinks:
1.3 数据集清洗
数据下载完成后,我们需要对其进行探索和清洗,此时你可能会发现一些原始数据之间的关系。如下图,展示了与维基百科图书中的页面关联性最强的wikilinks:
从上图可看出,排名前四的都是常用link,对构建推荐系统没有任何帮助。就像书籍的装订版本,是平装(paperback)还是精装(hardcover)对我们了解图书的内容没有任何作用,并且神经网络无法根据这个特征判别书籍是否相似。因此,可以选择过滤掉这些无用的特征。
(注:我们指的相似性是内容的相似性)
仔细思考哪些数据对构建推荐系统是有帮助的,哪些是无用的,有用的保留,无用的过滤,这样的数据清洗工作才算到位。
完成数据清洗后,我们的数据集中剩余41758条wikilinks以及37020本图书。接下来,我们需要引入有监督的机器学习方法。
二、监督学习
2.1 定义机器学习的任务
监督学习就是最常见的分类问题,即通过已有的训练样本去训练得到一个最优模型,再利用这个模型将所有的输入映射为相应的输出,对输出进行简单的判断从而实现分类的目的。基于我们预先给定的假设:类似的书籍会链接到类似的维基百科页面,我们可将监督学习的任务定义为:给定(book title,wikilink)对,确定wikilink是否出现在书籍的维基百科页面中。
我们将提供数十万个由书籍名称,wikilink以及标签组成的训练示例,同时给神经网络提供一些正确的训练示例,即数据集中包含的,以及一些错误的示例,以促使神经网络学会区分wikilink是否出现在书籍的维基百科页面中。
嵌入是为特定的任务而学习的,并且只与该问题有关。如果我们的任务是想要确定哪些书籍由Jane Austen撰写,嵌入会根据该任务将Austen所写的书映射在嵌入空间中更相邻的地方。或者我们希望通过训练来判断书籍的页面中是否有指定的wikilink页面,此时神经网络会根据内容使相似书籍在嵌入空间中相邻。
一旦我们定义了学习任务,接下来便可开始编写代码进行实现。由于神经网络只能接受整数输入,我们会将书籍分别映射为整数:
# Mapping of books to index and index to books
book_index = {book[0]: idx for idx, book in enumerate(books)}
book_index['Anna Karenina']
22494
(注:本文的代码是Git中的部分代码,完整代码的获取请参看GitHub)
对链接我们也进行同样的映射,并创建一个训练集。对所有书籍进行遍历,并记录页面上记录出现的wikilink,列出所有的(book,wikilink)对:
pairs = []
# Iterate through each book
for book in books:
title = book[0]
book_links = book[2]
# Iterate through wikilinks in book article
for link in book_links:
# Add index of book and index of link to pairs
pairs.extend((book_index[title],
link_index[link]))
最终有772798个示例用于模型训练。接下来,随机选择链接索引和book索引,如果它们不在(book,wikilink)对中,那么它们就是能用于增强模型的学习能力false examples。
2.2 关于训练集及测试集的划分
虽然在有监督的机器学习任务中需要划分验证集(validation set)以及测试集,但本文的目的不在于得到精确的模型,只是想训练神经网络模型完成预测任务。训练结束后,我们也不需要在新的数据集中测试我们的模型,所以并不需要评估模型的性能或者使用验证集以防止过拟合。为了更好的学习嵌入,我们将所有的示例都用于训练。
2.3 嵌入模型
神经网络嵌入虽然听上去十分复杂,但使用Keras深度学习框架实现它们却相对容易。
嵌入模型分为5层:
Input:并行输入书籍和链接
Embedding:设置代表book和link两个类别的向量长度为50
Dot:进行点积运算
Reshape:把点积reshape成一个一维向量
Dense:一个带sigmod激活函数的输出神经元
在嵌入神经网络中,能够通过训练权重最小化损失函数。神经网络将一本书和一个链接作为输入,输出一个0到1之间的预测值,并与真实值进行比较,模型采用Adam优化器。
模型代码如下:
from keras.layers import Input, Embedding, Dot, Reshape, Dense
from keras.models import Model
def book_embedding_model(embedding_size = 50, classification = False):
"""Model to embed books and wikilinks using the Keras functional API.
Trained to discern if a link is present in on a book's page"""
# Both inputs are 1-dimensional
book = Input(name = 'book', shape = [1])
link = Input(name = 'link', shape = [1])
# Embedding the book (shape will be (None, 1, 50))
book_embedding = Embedding(name = 'book_embedding',
input_dim = len(book_index),
output_dim = embedding_www.leyouzaixian2.com size)(book)
# Embedding the link (shape will be (None, 1, 50))
link_embedding www.furong157.com= Embedding(name = 'link_embedding',
input_dim = len(link_index),
output_dim = embedding_size)(link)
# Merge the layers with a dot product along the second axis
# (shape will be (None, 1, www.michenggw.com 1))
merged = Dot(name = 'dot_product', normalize = True,
axes = 2)([book_embedding, link_embedding])
# Reshape to be a single number (shape will be (None, 1))
merged = Reshape(target_shape = [1])(merged)
# Squash outputs for classification
out = Dense(1, activation = 'sigmoid')(merged)
model = Model(inputs =www.gcyl159.com [book, link], outputs = out)
# Compile using specified optimizer and loss
model.compile(optimizer www.gcyl152.com/= 'Adam', loss = 'binary_crossentropy',
metrics = ['accuracy'])
return model
这个框架可以扩展至各类嵌入模型。并且,我们并不关心模型是否精准,只想获取嵌入。在嵌入模型中,权重才是目标,预测只是学习嵌入的手段。
(注:这句话不明白的话,可以参看我之前介绍embedding的博客)
本模型约含400万个权重,如下所示:
__________________________________________________________________________________________________
Layer (type) Output Shape Param # Connected to
==================================================================================================
book (InputLayer) (None, 1) 0
__________________________________________________________________________________________________
link (InputLayer) (None, 1) 0
__________________________________________________________________________________________________
book_embedding (Embedding) (None, 1, 50) 1851000 book[0][0]
__________________________________________________________________________________________________
link_embedding (Embedding) (None, 1, 50) 2087900 link[0][0]
__________________________________________________________________________________________________
dot_product (Dot) (None, 1, 1) 0 book_embedding[0][0]
link_embedding[0][0]
__________________________________________________________________________________________________
reshape_1 (Reshape) (None, 1) 0 dot_product[0][0]
==================================================================================================
Total params: 3,938,900
Trainable params: 3,938,900
Non-trainable params: 0
利用上述方法,我们不仅可以得到书籍的嵌入,还可以得到链接的嵌入。
2.4 生成训练示例
神经网络是batch learners,因为它们是基于一小批样本进行训练的,对所有的数据批次都进行了一次迭代称为epochs。常用的神经网络训练方法是使用生成器,它能产生批量样本函数,优点是不需要将所有的训练集都加载到内存中。
下面的代码完整地显示了生成器:
import numpy as np
import random
random.seed(100)
def generate_batch(pairs, n_positive = 50, negative_ratio = 1.0):
"""Generate batches of samples for training.
Random select positive samples
from pairs and randomly select negatives."""
# Create empty array to hold batch
batch_size = n_positive * (1 + negative_ratio)
batch = np.zeros((batch_size, 3))
# Continue to yield samples
while True:
# Randomly choose positive examples
for idx, (book_id, link_id) in enumerate(random.sample(pairs, n_positive)):
batch[idx, :] = (book_id, link_id, 1)
idx += 1
# Add negative examples until reach batch size
while idx < batch_size:
# Random selection
random_book = random.randrange(len(books))
random_link = random.randrange(len(links))
# Check to make sure this is not a positive example
if (random_book, random_link) not in pairs_set:
# Add to batch and increment index
batch[idx, :] = (random_book, random_link, neg_label)
idx += 1
# Make sure to shuffle order
np.random.shuffle(batch)
yield {'book': batch[:, 0], 'link': batch[:, 1]}, batch[:, 2]
其中n_positive表示每个batch中正例样本的数量,negative_ration表示每个batch中负例样本与正例样本的比率。
在有监督的学习任务、生成器、嵌入模型都准备完毕的情况下,我们正式进入图书推荐系统的构建。
2.5 训练模型
有一些训练参数是可以调节的,如每个批次中正例样本的数量。通常,我会从一小批量开始尝试,直到性能开始下降。同样,我们需要通过尝试调整负例样本与正例样本的比率。
n_positive = 1024
gen = generate_batch(pairs, n_positive, negative_ratio = 2)
# Train
h = model.fit_generator(gen, epochs = 15,
steps_per_epoch = len(pairs) // n_positive)
一旦神经网络开始训练,我们就能获取权重:
# Extract embeddings
book_layer = model.get_layer('book_embedding')
book_weights = book_layer.get_weights()[0]
三、构建推荐系统
嵌入本身不那么有趣,无非是50维向量。
然而我们可以利用这些向量做些有趣的事,例如构建图书推荐系统。为了在嵌入空间中找到与所查询书籍最接近的书,我们取那本书的向量,并计算它与所有其他书的向量的点积。如果我们的嵌入是标准化的,那么向量之间的范围会从-1,最不相似,到+1,最相似。
(注:在代码里先进行了l2范数的标准化,因此,2个向量点积之后就是余弦相似度,范围从-1到1)
以查询《战争与和平》为例,相似书籍如下:
除了对书籍进行嵌入,我们也对wikilink也做了嵌入,以此查询与wikilink最为相似的链接:
再比如,目前,我正在阅读Stephen Jay Gould的经典著作《Bully for Brontosaurus》,将其输入构建的推荐系统便可以知道接下来应该读什么:
(注:通过上述的介绍,我们已经看到了如何利用神经网络的embeddding来构建推荐系统。本质上,这仍然是一个协调过滤的思想,即根据相似性来寻找某本书籍的最近邻居,然后把最近邻居推荐给喜欢某本书籍的人。但是,这里与传统的协调过滤方法明显的不同的地方有:
①基于embedding的推荐方法并不要求书籍向量的”同一化“,即某本书不需要再像以前那样,必须由长度完全相等的向量来表示,在现在世界中,构造这样的数据集很困难,即便构造出来,数据也很稀疏。
②基于embedding的推荐方法可以通过”关系“来捕捉相似性,以此来保证embedding后的向量仍然可以保证这种相似性。我们在这里理解维基百科的数据,可以认为书名到wikilinks存在着某种关系。那么给我们的启发是什么呢?比如,我们想求得成人奶粉这个类目下所有sku的相似性,我们就可以类似文中构造训练集的方法那样来构造关于成人奶粉sku的数据集。)
四、嵌入可视化
嵌入的优点是可以将所学到的嵌入进行可视化处理,以显示哪些类别是相似的。首先需要将这些权重的维度降低为2-D或3-D。然后,在散点图上可视化这些点,以查看它们在空间中的分离情况。目前最流行的降维方法是——t-Distributed Stochastic Neighbor Embedding (TSNE)。
我们将37000多维的图书通过神经网络嵌入映射为50维,接着使用TSNE将维数将至为2。
似乎有一些明显的团块。然而,由于我们没有以任何方式区分书籍,因此很难从上图中得出有任何意义的东西。
我们需要其它的一些信息让我们看到我们对图书做的embedding是有效的。
在数据集里,有个字段是图书的类别genre。
让我们用genre画出embeddings,它包含在每本书的Infobox模板数据中。我们将其限制为10种最流行的genres。
我们可以看到,经过embedding后的图书,原来类别很相似的,降维后仍然相似。
更多可视化的探索可以参看源代码。
五、总结
神经网络嵌入能够将离散的数据表示为连续的低维向量,克服了传统编码方法的局限性,能查找最近邻,作为另一个模型的输入以及进行可视化,是处理离散变量的有效工具,也是深度学习的有效应用。在本文中,我们基于链接到相似页面间彼此相似的假设,利用神经网络嵌入构建了图书推荐系统。
构建基于神经网络嵌入的推荐系统的步骤总结如下:
收集数据
制定一个有监督的学习任务
训练嵌入神经网络模型
进行推荐实战及可视化
参考文献
【1】基于神经网络嵌入的推荐系统:利用深度学习和维基百科构建图书推荐系统
【2】Wikipedia Data Science: Working with the World’s Largest Encyclopedia
【3】 Neural Network Embeddings Explained
【4】How to Use t-SNE Effectively
【5】诠释数据降维算法:一文讲尽t-分布邻域嵌入算法(t-SNE)如何有效利用
基于神经网络的embeddding来构建推荐系统的更多相关文章
- 基于 CDH 构建推荐系统
我理解的推荐系统本质是一种排序方式.排序的规则是按照我们预测的用户喜好程度的一个排序的列表,而如何定义用户的喜好程度是推荐系统要解决的核心问题.机器学习的算法只是推荐系统的一部分.构建一个完整的推荐系 ...
- 使用wrd2vec构建推荐系统
概览 完整的代码可以从这里下载: https://github.com/prateekjoshi565/recommendation_system/blob/master/recommender_2. ...
- DL4NLP——词表示模型(二)基于神经网络的模型:NPLM;word2vec(CBOW/Skip-gram)
本文简述了以下内容: 神经概率语言模型NPLM,训练语言模型并同时得到词表示 word2vec:CBOW / Skip-gram,直接以得到词表示为目标的模型 (一)原始CBOW(Continuous ...
- 基于webpack+react+antd 项目构建
工欲善其事必先利其器,学习React也是如此. 下面分享一篇基于webpack+react+antd 项目构建的好文章, https://blog.hduzplus.xyz/articles/2017 ...
- Cola Cloud 基于 Spring Boot, Spring Cloud 构建微服务架构企业级开发平台
Cola Cloud 基于 Spring Boot, Spring Cloud 构建微服务架构企业级开发平台: https://gitee.com/leecho/cola-cloud
- 如何基于 K8S 多租能力构建 Serverless Container
当前 Kubernetes 已经成为名副其实的企业级容器编排规范,很多云平台都开始提供兼容 Kubernetes 接口的容器服务.而在多用户支持方面,多数平台选择直接提供专属虚机集群,用户需要花费大量 ...
- 【甘道夫】通过Mahout构建推荐系统--通过IDRescorer扩展评分规则
通过Mahout构建推荐系统时,假设我们须要添�某些过滤规则(比方:item的创建时间在一年以内),则须要用到IDRescorer接口,该接口源代码例如以下: package org.apache.m ...
- 基于jersey和Apache Tomcat构建Restful Web服务(二)
基于jersey和Apache Tomcat构建Restful Web服务(二) 上篇博客介绍了REST以及Jersey并使用其搭建了一个简单的“Hello World”,那么本次呢,再来点有趣的东西 ...
- 基于jersey和Apache Tomcat构建Restful Web服务(一)
基于jersey和Apache Tomcat构建Restful Web服务(一) 现如今,RESTful架构已然成为了最流行的一种互联网软件架构,它结构清晰.符合标准.易于理解.扩展方便,所以得到越来 ...
随机推荐
- [转]C#综合揭秘——细说多线程(下)
引言 本文主要从线程的基础用法,CLR线程池当中工作者线程与I/O线程的开发,并行操作PLINQ等多个方面介绍多线程的开发. 其中委托的BeginInvoke方法以及回调函数最为常用. 而 I/O线程 ...
- [转]VC++中对文件的写入和读取
本文转自:http://blog.csdn.net/fanghb_1984/article/details/7425705 本文介绍两种方法对文件进行读取和写入操作:1.采用fstream类:2.采用 ...
- CF983A Finite or not?
思路: 如果p,q不互质,先把q除以p,q的最大公约数.接下来只要b中包含了所有q的质因子就可以了.可以在gcd(q, b) 不等于1的时候不断地用q除以gcd(q, b).最后如果q等于1,说明Fi ...
- mysql5.7.25集群部署和方案设计(附PXC一键部署脚本)
还记得我们之前部署mysql集群有多麻烦嘛?波哥来救你们啦!~ 我已将项目上传到了我的github仓库中,大家可以点击仓库地址出现的连接登录查看相应的代码!如果觉得不错别忘了转发.点赞哦! 部署步骤: ...
- Proteus与Keil连接及其仿真(有例子哦!)
记录一下Proteus仿真的一些设置和使用,以方便自己以后复习和大家交流!如有错误,希望大家指正. 1.Proteus软件的安装,这里就不作说明了.
- LC.exe 已退出,代码为-1 问题解决
最近一个c#工程,之前编译正常.后重装系统,安装DevExpress后,编译一直失败,并提示"4>C:\Windows\Microsoft.NET\Framework\v4.0.303 ...
- pycharm激活码 pycharm安装后激活方式 pycharm汉化包安装
汉化包 下载地址: 链接:http://pan.baidu.com/s/1pL6xWl9 密码:x1fh 将下载好的文件解压:将resources_cn.jar放到安装目录下的lib目录下即可 重启 ...
- Elasticsearch学习(二)————搜索
Elasticsearch1.query string search1.1.搜索全部// 1. GET http://ip:9200/test/test/_search 结果: { "too ...
- sh脚本写法
1.shell注释符号: 1. 单行注释: “#” 2. 多行注释: : << ! 语句1 语句2 语句3 语句4 ! http://blog.csdn.net/lansesl2008/a ...
- 筛选法 || POJ 3292 Semi-prime H-numbers
5,9,13,……叫H-prime 一个数能且仅能由两个H-prime相乘得到,则为H-semi-prime 问1-n中的H-semi-prime有多少个 *解法:vis初始化为0代表H-prime, ...