原创:张春阳

这篇文章是偶然的机会在看一个 Conference 的时候听到的,当时第一感觉是 StarSpace,哇,在到处都是各种看不懂的字母堆叠的算法名称里,这名字给人的感觉眼前一亮,让我想去看看这篇文章。
一查才知道,文章也是师出有门,是 Facebook AI Research 发表在 2017 年的一篇文章,文章的名字也很有意思, StarSpace: Embed All The Things! , 要 embed 一切,目标够大!我喜欢!

一直以来,我都很喜欢工业界出的文章,因为总是可以最高效的从里面学习到值得在工作中尝试的 Idea,这篇文章也是这样,实现的方法并不是很难,但是却很好用,并且能够在不同的场景中使用,确实是 embed 一切。说了这么多,你是不是也和我一个表情了,let's go!
这篇文章主要的时间线是这样的:
- StarSpace 是用来解决那些问题的?
- StarSpace 是怎么做的呢?
- 怎么快速的在工作中用起来 StarSpace?
StarSpace 是用来解决那些问题的?
在自然语言处理中,我们有大量的文本、文章、实体、标签等一大堆东西,我们希望能够通过向量把他们表示出来。因为如果我们能把他们都表示成在同一个空间内的向量,我们就可以对它们做相似度的判断,做分类的工作,做排序的工作了。在基础的任务之上,我们就能够使用它们来构建内容推荐,文本分类、打标签等等的系统了。这些场景在我们现实工程中的意义那就太大了,可以说现在任何一家互联网公司,都存在这些场景,但只是说大家解决的问题的领域可能不一样。

所以,我们可以说,对工业场景中的各种个样的对象做 embeding,是目前在应用场景中能够解决很多问题的基石。这种思路不仅仅在自然语言中,针对一些文字有效,其实在机器视觉的领域也是同样的,都是要想办法把实体映射到某一个向量空间中。
StarSpace 就是这样一种,把各种各样的数据投射到向量空间中的方法。数据包括但不限于:文本、文章、主题、标签、知识图谱等。
StarSpace 是怎么做的呢?
接下来,我们来看看 StarSpace 是怎么做的呢?
这里我们使用一个具体的例子,来说明 StarSpace 的工作原理。假设你现在是一家外卖公司的算法工程师,你的老板希望你能把用户对餐馆的评论进行向量化表示,从而能够实现对于用户的评论打标签的功能。老板通过其他部门收集来的数据就是一些用户的评价,以及花钱请一些“人工只能训练师”标注的标签。 标注的形式如下图所示, 表示评论,
表示标签,并且已经标记好了哪些评论和哪些标签是有关系的。比如下图中
就和
这三个标签有关系。

这里我们换一种形式来表达这种关系,我们使用一种叫共现矩阵的方法来表示,把上面的图中数据的关系表示成下面这样:

共现矩阵其实很好理解,它就是表示了 这个评论和
有关系。通过这样的方法,我们也可以把其他的评论和标签之间的关系也表示进来。就像下面这张图一样:

通过上面的表格,我们可以看到 和
他们都有同一个标签
,
没有
,我们可以通过下面这张图来表示这样的一种关系:

我们希望 和
在向量空间的表示和
的表示接近,而
和
的距离较远。我们通过不断的计算,就能够形成一个又一个的簇,就像天空中一个又一个的星座群一样,所以这个算法也叫做StarSpace。
那么,我们怎么能够求解出这些星座呢?
我们可以使用 bag-of-word 来表示评论,比如说 把 表示成
,这里面的每一个位置表示词典里的 token。我们使用 one-hot 来表示标签,把
这个标签表示为
。我们可以通过下面的结构,把这两者给关联起来:

上面这个结构图的整体的含义就是,我希望 这个评论的向量能够和
这个标签比较的接近,这里比较接近我们使用余弦相似度来计算。为了能够让评论向量和标签向量进行计算,我们需要让他们的维度相同,由于他们的 sparse 表示维度不同,所以我们需要在输入和 dense layer 中间增加一个 feed forward 层来让维度统一。然后就是这里的 LABEL 了,使用 0/1 表示的就是在共现矩阵中,是不是有这样的关系。当然,我们现在只有 positive example,同样的我们也可以做一些 negative sampling,把它们也放到我们的模型中进行运算。

下面图片中红色的线表示反向传播的过程,这些误差的信息就能够帮助我们学习到评论和标签的向量表示,让它们构成我们的星空。

最后,我们来看看最终学习到的星空的样子:

我们可以看到,通过 StarSpace 的学习,我们得到了 Italian、Pizza 和 Veggie 的星座,并且也得到了能够生成这些星座的模型参数。有了这些参数,我们就能够解决老大交给我们的任务了,给评论自动的打标签。不但实现了这个功能,我们也可以在新建标签的时候,自动的找到符合这个标签的评论了。当然,我们也可以使用它来对用户做推荐,用户喜欢某个 topic 的评论,有可能对它周边的评论和标签也感兴趣。

官方给出的在文本分类的测试结果如下图所示,略微强与 FastText 一丢丢,在其他任务上的测试结果,大家可以去看看原文哈。
看完这些,是不是已经跃跃欲试了呢,下面让我们看看怎么用起来 StarSpace 吧。
怎么快速的在工作中用起来 StarSpace?
这里我们使用 Facebook 官方实现的代码来使用 StarSpace,由于官方实现的版本使用 C++ 来实现,所以我们不能直接在 python 里使用,好在项目中提供了 python wrapper 可以方便我们在 python 的项目中使用。
官方实现的代码仓库地址如上,为了能够使用 python 进行调用,我们先要按照官方文档中的操作,使用 python binding 给 c++ 的功能代码,提供 python 接口,编译出动态链接库.so(如果大家对这块感兴趣,可以留言, 这种方法也是经常在高性能的场景中使用的)。
OK,那让我们开始动起来把。
# 克隆官方代码仓库
git clone https://github.com/facebookresearch/StarSpace.git
# 安装 boost(一个c++的包)
$wget https://dl.bintray.com/boostorg/release/1.63.0/source/boost_1_63_0.zip
$unzip boost_1_63_0.zip
$sudo mv boost_1_63_0 /usr/local/bin
# 安装 cmake
https://cmake.org/install/
# 安装 conan
https://conan.io/downloads.html
# 进入项目目录下的 python 文件夹
Starspace/python
# 如果你使用的是 anaconda 等虚拟环境,需要在 CMakeLists.txt 文件14行后增加如下两行
# PYTHON_INCLUDE_DIR 这个目录中需要有 Python.h 这个头文件,你可以先检查看看
vim CMakeLists.txt
set(PYTHON_LIBRARY "虚拟python环境地址/lib")
set(PYTHON_INCLUDE_DIR "虚拟python环境地址/include/python3.8")
# 安装 numpy,编译完成后的测试中需要
pip install numpy
# 执行编译操作
chmod +x build.sh
./build.sh
如果执行完上面这些没有报错,那么你就大功告成了,这个时候你在 Starspace/python/test 中应该能看到 starwrap.so 这个动态链接库,这个文件就是我们使用 python 调用c++ StarSpace 的 wrapper。还有两个 test.py 这个文件,这个文件中就是用来测试 starwrap.so 这个动态链接库中的接口的脚本。
那么,我们来看看这个脚本,我们该怎么使用 StarSpace 做训练和生成 embedding 的向量。
# 导入 starwrap.so 包
import starwrap as sw
# 设置 starSpace 对象参数
# 参数的种类和解释可以参看这里 https://github.com/facebookresearch/StarSpace#full-documentation-of-parameters
arg = sw.args()
训练 StarSpace 模型
import starwrap as sw
# 设置 starSpace 对象参数
arg = sw.args()
# 训练样本文件
arg.trainFile = './input.txt'
# 测试样本文件
arg.testFile = './input.txt'
arg.trainMode = 5
# 实例化 starSpace 对象
sp = sw.starSpace(arg)
# 初始化对象
sp.init()
# 训练 starSpace 模型
sp.train()
# 保存模型
sp.saveModel('model')
sp.saveModelTsv('model.tsv')
StarSpace 支持多种不同类型的 embedding, 我们可以通过调整 trainMode 和 trainFile 的格式来让 StarSpace 完成不同类型的任务。
我们来看看 StarSpace 可以做的任务的类型:
- 文本分类
- 文本搜索
- 推荐系统
- 语句和文章关系
- 实体关系
- 图关系
下面是在做不同任务的时候,我们要设置的 trainMode 的类型和相应的输入的格式(在超链接中)。
这里有两个名词需要先解释一下,LHS(stands for left-hand-side)表示输入,RHS(stands for right-hand-side)表示标签。
- trainMode = 0:
- 每个样本中包含 input 和 labels
- 如果 fileFormat 是 fastText,这些 labels 是独立的 features/words (格式就类似于word_1 word_2 ... word_k label__1 ... label*r,*label后接的是 label 的名字)
- Use case: 分类任务

https://github.com/facebookresearch/StarSpace#tagspace-word--tag-embeddingsgithub.com
- 如果 fileFormat 是 'labelDoc',这些 labels 就是 bag of featuresthen the labels are bags of features, and one of those bags is selected (格式类似于word_1 word_2 ... word_k <tab> label_1_word_1 label_1_word_2 ... <tab> label_r_word_1 ..,label_r_后接的是 word)
- 如果 fileFormat 是 'labelDoc',这些 labels 就是 bag of featuresthen the labels are bags of features, and one of those bags is selected (格式类似于word_1 word_2 ... word_k <tab> label_1_word_1 label_1_word_2 ... <tab> label_r_word_1 ..,label_r_后接的是 word)
- Use case: 搜索任务,可以参考 ArticleSpace
- trainMode = 1:
- 每个样本中包含几个 lables。在训练时期,每次都随机选取一个 lable 作为 RHS,其他的就作为 LHS。
- Use case: 基于内容的或者基于协同过滤的推荐系统

https://github.com/facebookresearch/StarSpace#pagespace-user--page-embeddingsgithub.com
- trainMode = 2:
- 每个样本中包含几个 lables。在训练时期,每次都随机选取一个 lable 作为 RHS,其他的就作为 LHS。
- Use case: 学习一个 mapping,某个 object 属于一些列 object 中的哪一个,比如说sentence (from within document) to document。
- trainMode = 3:
- 每一个样本包含一些列的 lables。在训练时期,从这些 labels 中随机的选择两个,作为 LHS 和 RHS。
- Use case: 可以学习一对 objects 之间的关系,比如说 sentence to sentence 任务。

https://github.com/facebookresearch/StarSpace#sentencespace-learning-sentence-embeddingsgithub.com
- trainMode = 4:
- 每一个样本包含两个 labels。在训练时期,第一个 label 会被选为 LHS,第二个 label 会被选为 RHS。
- Use case: 学习多关系的图关系

- trainMode = 5:
- 每个样本只包含 input。 在训练时期,它生成多个训练样本:每个输入输入的特征被选为 RHS, 其他它们周围的特征被选为 LHS。
- Use case: 使用无监督的方法学习 word 的 embedding。
使用训练好的 StarSpace 模型
import starwrap as sw
import numpy as np
# 实力化并加载 starspace 模型
sp = sw.starSpace()
sp.initFromSavedModel('model')
sp.initFromTsv('model.tsv')
# 寻找与该文本相关的 10 个文本
sp.nearestNeighbor('some text', 10)
# 打印 doc 的向量
print(np.array(sp.getDocVector('this\tis\ttest', '\t')))
print(np.array(sp.getDocVector('this is test', ' ')))
可视化你的结果
你可以把你训练好的模型保存为 tsv 文件格式,并用 tensorflow projector 生成可视化的结果。

以上就是这个浪漫的算法 StarSpace,如果你还有对 StarSpace 感兴趣的话题,欢迎留言👏👏
网友评论