日志

Spark 大规模稀疏矩阵乘法

spark 可以通过 BlockMatrix 进行矩阵相乘,但其在大规模稀疏矩阵场景有非常严重的性能问题,本文通过基于 RDD 和 DataFrame 两种方式实现基于 spark 的大规模稀疏矩阵乘法运算。

一、矩阵乘法运算

1

二、通过 BlockMatrix 进行矩阵相乘

3

from pyspark.mllib.linalg.distributed import *
from pyspark.sql import SparkSession

ss = SparkSession.builder.appName("test") \
    .config("spark.serializer", "org.apache.spark.serializer.KryoSerializer") \
    .getOrCreate()

sc = ss.sparkContext
sc.setLogLevel("WARN")

M_rdd = sc.parallelize([(0,0, 1), (0,1, 2), (0,2, 3), (1,0, 4), (1,1, 5), (1,2, 6)])
N_rdd = sc.parallelize([(0,0, 7), (0,1, 8), (1,0, 9), (1,1, 10), (2,0, 11), (2,1, 12)])
M = CoordinateMatrix(M_rdd).toBlockMatrix()
N = CoordinateMatrix(N_rdd).toBlockMatrix()
M.multiply(N).toCoordinateMatrix().entries.collect()

######## 输出 #############
# [MatrixEntry(0, 0, 58.0),
#  MatrixEntry(1, 0, 139.0),
#  MatrixEntry(0, 1, 64.0),
#  MatrixEntry(1, 1, 154.0)]

三、BlockMatrix 的性能问题

在 spark 的官方文档中关于 multiply 这个方法的描述如下

Left multiplies this BlockMatrix by other, another BlockMatrix. The colsPerBlock of this matrix must equal the rowsPerBlock of other. If other contains any SparseMatrix blocks, they will have to be converted to DenseMatrix blocks. The output BlockMatrix will only consist of DenseMatrix blocks. This may cause some performance issues until support for multiplying two sparse matrices is added.

也就是说,BlockMatrix 在进行矩阵乘法时会先把稀疏矩阵转换成稠密矩阵!!对于一个 10000 x 10000 的稀疏矩阵,实际存储的可能只有几万个非零元素,而转换成稠密矩阵后,你需要对所有 10000 x 10000 = 1亿 个元素提供存储空间!!而实际场景面对的稀疏矩阵的维度远远大于 10000,所以 BlockMatrix 无法适用于大规模稀疏矩阵运算。

四、矩阵乘法公式

CodeCogsEqn
图片 1
从矩阵乘法公式可知,矩阵相乘主要有以下环节:
1.左矩阵的列号(j)与右矩阵的行号(j)相同的元素进行两两相乘得到 MN_ik。
2.对所有具有相同下标(ik)的 MN_ik 进行相加,即得到 P_ik。

五、基于 RDD 实现矩阵乘法

阅读全文

日志

LightGBM

LightGBM 是一种基于决策树算法的快速 、 分布式 、 高性能的 GBDT 框架,它是传统 GBDT 算法的一种改进实现,“Light”主要体现在三个方面,即更少的数据 、 更少的特征 、 更少的内存,分别通过 GOSS(单边梯度采样)、 EFB(互斥特征捆绑)和 Histogram(直方图算法)三项技术实现。

GOSS (Gradient-based One Side Sampling 单边梯度采样)

GOSS 是一种采样方法,在每次迭代前,先对样本进行采样,保留梯度变化最大的 a% 个样本,为了不改变样本分布,还需要从剩下的梯度变化较小的样本中随机采样 b% 个样本,并给予这 b% 个样本 (1-a)/b 的权重,这两部分样本为最终的训练样本。通过 GOSS 既保留了重要的样本,又在保持样本分布的同时大大减少了训练样本数,从而实现不影响模型准确性的同时大幅度提升训练效率。

GOSS 为什么以梯度作为样本权重?

因为 GBDT 对 loss 的负梯度进行拟合,所以样本误差越大,梯度的绝对值越大,证明模型对该样本的学习还不足够,相反如果越小证明模型对该样本的学习已经很充分,所以梯度的绝对值越大,样本重要性越高。(梯度是某一点最陡峭的地方,梯度大小形容它有多陡峭)

EFB(Exclusive Feature Bundling 互斥特征捆绑)

对于具有高维稀疏特征的数据,很多特征是互斥的(即多个特征之间最多只有一个特征的取值为非 0),EFB 通过捆绑多个互斥特征形成一个“大特征”,从而大大减少特征的数量,相当于是一种降维的方法。

例如,特征 A 的取值为 0~10,特征 B 的取值为 0~20,A、B 为互斥特征,那么捆绑 A/B 形成特征 C,特征 C 的取值为 0~30,所以 B=5 与 C=15 是等价的。

构造特征直方图是训练 GBDT 的主要时间消耗,而构造特征直方图的时间取决于需要遍历的特征数量,通过 EFB 方法可以减少特征的数量从而加快训练的效率。

备注:找出最优的 bundle 组合数是一个 NP 问题,LightGBM 通过贪心近似算法解决。即转化为图着色问题,图中的点为特征,非互斥的特征用一条边连接,边的权重为相连特征的总冲突值,那么着色相同的点即为互斥点(特征)

直方图优化算法(Histogram)

LightGBM 基于直方图算法优化查找最佳分割点的效率,在训练前首先通过直方图算法将连续特征离散化,相当于对特征的值进行分段划分,形成 k 个 bins(即 k 个离散值),构造一个宽度为 k 的直方图,在遍历数据时,根据离散值在对应 bin 上累积统计量(即梯度 g,样本数 n)。通过累积的统计量计算分裂增益,通过遍历所有 bin 寻找该特征的最佳分割点。

流程总结

  1. 特征离散化,得 k 个 bins,形成宽度为 k 的直方图
  2. 遍历数据,根据离散值在对应 bin 上累积统计量(g、n)
  3. 遍历 bins,通过累积统计量计算不同划分的分裂增益,求得最佳分割点

增益计算公式:

其中 S 为梯度之和,n 为样本数,L/R/P 表示左 / 右 / 父节点

优点:

  1. 传统的 pre-sorted 算法(例如 XGBoost 的 exact greedy 算法)在计算某个特征的分裂增益时需要遍历所有的特征值的划分情况,而直方图算法只需要遍历 k 个 bins 的划分情况,时间复杂度从 O(#data * #features) 降到 O(#bins * #features),大大减少了计算量。
  2. 传统的 pre-sorted 算法需要对特征预排序,而直方图算法没有排序要求,进一步提升了效率。
  3. 直方图算法可以通过做差加速,即只需要知道父节点的直方图,和任一子节点的直方图,即可通过做差得到其兄弟节点的直方图,效率提升一倍。
  4. 由于直方图算法没有排序要求,因此不用额外存储排序索引,另外离散化后的特征值可以用更小的数据类型表示(例如 256 个 bin 则只需要用 8 位整型表示,即从通常的 4 字节降到 1 字节),以上两点都可以大大减少内存的占用。

分析:直方图算法主要围绕着训练更快 、 内存占用更少两个方面进行优化,虽然它找到的并不是最精确的分割点,但对最终的模型精度影响并不大,而且较粗的分割点本身也有正则化的效果,有时甚至会有更好的精度。另外,对于 bosting 框架而言,决策树本身是弱模型,单棵树的误差变化稍大,对最终的结果没有太大的影响。

并行学习(Parallel Optimization)

阅读全文

日志

XGBoost

xgboost 是对传统 GBDT 算法的一种改进实现,主要包括损失函数 、 正则化 、 分裂点查找优化 、 稀疏特征感知 、 并行化等方面.

原理推导

假设迭代训练 k 次,那么 xgboost 的模型函数可以表示为:

其中 k 表示决策树的数目,f 表示一棵 CART 树。那么,前 t 棵树的预测值可以表示为:

需要优化的目标函数为:

那么训练第 t 棵树的目标函数可表示为:

在前 t-1 棵树处进行二阶泰勒展开:

由于去除常数项 L 后不影响问题优化,因此目标函数化简为:

xgboost 定义正则项如下:

其中 T 为叶子数,wj 为第 j 个叶子节点的预测值(权重,prediction score)。目标函数为:

对于样本 xi ,在第 t 棵树的预测值表示为:

其中 q 表示树结构, q(xi) 表示样本 xi 在中被分到的叶子节点索引,wj 表示第 j 个叶子节点的预测值,因此原目标函数由样本表示形式(sample-wise)改写成树结构表示形式(structure-wise):

化简得:

其中 Gj 和 Hj 分别表示被分到第 j 个叶子节点的所有样本的 loss 的一阶(二阶)导数值之和,wj 表示第 j 个叶子节点的预测值(权重),T 表示叶子节点数。

对目标函数求关于 wj 的导数等于 0 即可得最优的预测值 wj^* 使得目标函数最小化:

将 wj^* 代入目标函数 obj^(t) 得 xgboost 的终极目标函数:

上式度量了一棵结构为 q(x) 的树的好坏,值越小越好!!!!

例子:

实际上我们无法枚举所有的树结构然后选择最优的树,xgboost 通过逐层优化的方式构建树模型。将一个叶子节点分裂为左右两个新的叶子节点带来的增益可以表示为:

上式就是 xgboost 的特征选择准则, Gain 越大代表分裂后带来的 loss 减少量越多,所以 Gain 越大越好.

策略:当 gain>0 或大于某个阈值时进行分裂,否则不分裂(相当于剪枝)

分裂点查找算法(split finding algorithm)

阅读全文

日志

基于 CCO 的协同过滤推荐

基于 CCO(Correlated Cross-Occurrence) 的协同过滤本质上是一种 Item-Based CF 算法

基于 CCO 的协同过滤推荐

基于 CCO 的协同过滤推荐通过物品之间的共现情况来计算物品之间的关联度,它跟一般的协同过滤算法不同的地方在于一般的协同过滤只能针对单一行为,而CCO算法可以计算交叉行为下的协同关联。

例如:它不仅可以通过用户的浏览行为来告诉你 “浏览了内容A的人可能会浏览内容B” ,它还能结合用户的浏览行为和用户的广告点击行为来告诉你 “点击了广告A的人可能会浏览内容F”

基于单一行为

假设有以下用户浏览行为日志:
用户行为log

整理后得到以下关系:
u1=> [ t1, t2, t3, t5 ]
u2=> [ t1, t3, t4, t5 ]
u3=> [ t2, t4 ]

构建 “用户关于浏览帖子” 的矩阵 V 以及对应的转置矩阵 V^T:
mtV

将 矩阵V^T 乘以 矩阵 V 即可得到浏览帖子的共现矩阵:
mtVV

对数似然比(Log Likelihood Ratio)即LLR。我们根据两个事件的共现关系计算LLR值,用于衡量两个事件的关联度:
LLR
阅读全文

日志

常用推荐算法比较

在推荐系统中常用的推荐算法一般可以分为两类,即 基于内容推荐 以及 协同过滤。另外,还有一类算法专门处理冷启动问题,例如:基于全局最优推荐

基于内容推荐

基于内容推荐(Content-based Recommendations)非常好理解,简单来说就是根据用户偏好的内容给他推荐其他相似的内容。

cb

图:基于内容推荐

例如:从用户画像我们发现某个用户比较喜欢活跃在“音乐”、“体育”、“动漫”、“影视” 这些栏目,那么我们就会更倾向推荐这些栏目的内容给他,我们还发现他平时偏好的是关于 “NBA”、“美剧”、“邓紫棋” 等方面的内容,那么跟这些相关的内容就会有更高的推荐权重。

评价

基于内容推荐的结果一般具有很强的解释性,因为它推荐的就是强相关的内容,但这种强相关的特点也会导致一个很明显的缺陷,它缺乏惊喜度,因此它很难挖掘用户潜在的兴趣。要解决惊喜度的问题,可以采用另一类算法–协同过滤

协同过滤

协同过滤(Collaborative Filtering)推荐本质上也是一个找相似的过程,但它认为的相似不是指物品在属性上的相似,而是指在用户行为的层面上这些物品是否有关联,协同过滤一般可以分为 基于用户的协同过滤(User-CF)基于物品的协同过滤(Item-CF)

用户物品偏好

图:用户物品偏好

基于用户的协同过滤

解释:因为 用户1用户2 都喜欢物品A、B、C、D、E,所以认为 用户1用户2 是兴趣相似的用户,现在发现 用户2 还喜欢 物品F 所以我们认为 用户1 很可能也对 物品F 感兴趣,所以向 用户1 推荐 物品F。

基于物品的协同过滤

解释:因为喜欢 物品A 的大多数都喜欢 物品C,所以可以认为 物品A 和 物品C 是相似的。用户4 喜欢 物品A 所以向 用户4 推荐 物品C。

评价

协同过滤集合了群体智慧,能满足推荐惊喜度,善于发掘用户潜在的兴趣。训练的用户历史行为数据越多,一般训练出来的模型效果也会越好。协同过滤推荐的解释性一般较弱,推荐结果不如基于内容推荐算法直观,当然这是算法特点导致的,不直观不等于不正确
阅读全文

日志

个性化推荐系统的基本抽象

在大多数 UGC、PGC、OGC 平台中,“推荐”随处可见,本文主要介绍个性化推荐系统的抽象组成。

关于推荐

人工 VS 个性化

  • 早期的推荐功能大多以人工筛选为主。人工筛选可以确保内容的高质量,这是主要的优点之一,但人工筛选往往需要投入大量的人力成本。另外,由于不同用户的个人偏好差异巨大,高质量的内容往往不等于最合适的内容(例如:一篇介绍奢侈品牌化妆品的“高大上”内容对于一位平时只关心美食和户外运动的用户而言可能是毫无吸引力的)。

  • 为了提升用户体验,后来出现了“个性化内容推荐”的概念,通过引入个性化推荐系统,解决这类“千人千面”的问题。

推荐系统抽象

个性化推荐系统一般有三大环节:预处理 -> 召回 -> 排序
注:也可以认为是两层(召回 -> 排序)

预处理

第一个环节是预处理,预处理指的是对各种数据源的数据进行特征提取和特征构建,例如:内容特征提取,用户行为画像构建。

召回

第二个环节是召回,召回就是把预处理产生的特征作为输入参数,训练出推荐模型,然后使用推荐模型得出候选集合的过程。常用的召回方式有:基于内容推荐、基于协同过滤推荐等。

排序

第三个环节是排序,简单来说就是将候选集合根据一定的规则,例如:点击预估、匹配关联度、人为权重等进行调整,从而影响最后的推荐顺序。

推荐系统架构

最后简单画了一个基本的推荐系统架构原型
个性化推荐系统框架

图:个性化推荐系统架构 ©️hejunhao.me
转载请注明出处:

© http://hejunhao.me

日志

基于词向量的文本分类推断

之前的文章中介绍过提取文本标签特征(关键词)的几种算法TF-IDFTextRankTWE, 提取到标签特征后,我们可以进一步推断文本的内容分类。本文主要介绍通过词向量模型进行内容分类的一般思路。

提取文本标签特征

假设有以下一段文本:

2016/17赛季欧冠决赛在威尔士卡迪夫千年球场打响,最终尤文图斯以1-4不敌皇家马德里,遗憾错失冠军。赛后,尤文门将布冯表示对结果非常失望,因为尤文已经做了所有能做的事情。

通过关键词提取算法我们提取到以下标签:
#欧冠#决赛#尤文图斯#皇家马德里#布冯#门将#球场#冠军

假设我们有一个关于体育的分类体系:
分类体系

图:分类体系
  • 一级分类:体育
  • 二级分类:篮球(关联标签:NBA,CBA,篮球,篮板球,助攻,盖帽,FIBA,姚明,乔丹,三双…)
  • 二级分类:足球(关联标签:世界杯,亚冠,欧冠,中超,足球,英超,西甲,梅西,里皮,马拉多纳,门将,广州恒大,曼联…)

分类推断

通过词向量模型(Word2Vec)我们可以计算两个词之间的相似度(余弦距离):

Similarity(tagA, tagB) = cos(tagA_Vec, tagB_Vec)

因此,计算文本与分类的相似度实际上就是计算文本的标签与各个分类的关联标签的相似度。
我们发现上面这段文本与足球的相似度大于与篮球的相似度:

Dist(doc_tags, soccer_tags) > Dist(doc_tags, basketball_tags)

所以推断它是关于足球的内容,再进一步把它归类到体育这个一级分类。

转载请注明出处:

© http://hejunhao.me

日志

基于 TWE 模型的关键词提取

在之前的两篇文章中分别介绍了两种常见的关键词提取算法:TF-IDFTextRank。实际上不管是 TF-IDF 还是 TextRank,它们都没有考虑文本的语义,也就是文本的内容意义。为了进一步提升效果,我们引入主题模型(LDA)和词向量模型(word2vec)也就是TWE算法。

TWE (Topical Word Embedding)

主题模型

LDA

图:主题向量模型
  • 在主题模型中每个主题实际上是一系列代表该主题的词的分布 ,例如:上图主题 Topic#1 包含的主题词分布主要有 “红包”、“微信”、“支付宝”、“玩法”等,文字大小代表该词的概率分布差异,可以推断 Topic#1 是一个关于新年红包活动的主题。

词向量模型

word2vec

图:词向量模型
  • 在词向量模型中每个词都有自己的坐标,关联度越高的词相互的距离越近。例如:“梅西” 和 “足球” 的距离比 “奥巴马” 与 “足球” 的距离要近得多,所以 “梅西” 与 “足球” 的关联度相比 “奥巴马” 更高。

基于 TWE 提取关键词

  1. 通过主题模型我们可以知道一段文本的主题分布:P(topic | doc)
  2. 结合词向量和主题向量,我们可以通过余弦函数计算两者的距离:cos(word_vec, topic_vec)
  3. 通过计算词向量与该文本所有关联主题的主题向量的余弦相似度,最终得到词与文本的语义关联度:
Sim\displaystyle\text{(word, doc)}=\sum_{k=1}^K \cos(\text{word\_vec, topic\_vec}_{_k}) \times P(\text{topic}_{_k} \mid \text{doc})

算法比较

假设有以下一段文本:
文本
分别对该文本采用 TF-IDFTextRank 以及 TWE 算法提取关键词,结果如下:
算法对比

效果评价

  1. TF-IDF 更倾向于高频词。
  2. TextRank 综合考虑文本结构和词频,但它致命的问题在于,头尾的信息由于只有单边的入度,容易被抛弃掉,例如本例中的核心词 “pagerank”。
  3. TWE 更强调语义相关性,实际的提取效果也优于前面两种方式。

论文参考

转载请注明出处:

© http://hejunhao.me

日志

NLP 关键词提取算法之 TextRank

对文本提取关键词,除了经典的 TF-IDF 算法,还有另一种常用的算法 — TextRank

TextRank 介绍

TextRank 是一种基于图的排序算法,它的基本思想来自于著名的 Google PageRank.

PageRank via @Wikipedia

图:PageRank via @Wikipedia

PageRank 的基本思想

  • 一个网页的入链越多,它就越重要。
  • 如果一个网页被越重要的网页所指向,它也越重要。

TextRank 算法简释

下图是通过 TextRank 进行关键词提取的一个简单例子
例子

图:通过 TextRank 提取文本关键词

通过 TextRank 进行关键词提取的主要步骤

  1. 对文本进行分词,并去掉停用词以及非目标词性词汇。
  2. 通过一个固定长度的滑动窗口在分词文本上滑动,每个窗口内的词根据共现关系构建一条边,最终形成一个图。
  3. 对图进行 PageRank,实际上就是投票和迭代的过程。
  4. 选出权重最高的 TopK 个词。

算法总结

  • TextRank 算法一定程度上考虑了文本中词与词之间的关系,也就是文本结构,在实际应用中它的效果一般会比 TF-IDF 好,当然它的计算复杂度也比 TF-IDF 高得多。
  • TextRank 算法更擅长处理长文本,对短文本的效果并不理想,主要因为短文本的词汇信息较弱,构建的图并不理想。
  • TextRank 算法仍然倾向于选择出现较为频繁的词作为关键词。
转载请注明出处:

© http://hejunhao.me

日志

NLP 关键词提取算法之 TF-IDF

TF-IDF 是处理 NLP 问题时经常使用的经典算法,常用于对文本进行关键词的提取。

TF-IDF 介绍

计算公式

TF-IDF 的计算非常简单


TF-IDF = TF(\text{文章词频}) * IDF(\text{逆文档频率})

概念解释

TF: 指某个词在一段文本中出现的词频


TF = \dfrac{\text{某个词在文章中出现的次数}}{\text{文章总词数}}

IDF: 指某个词在所有文本(对应语境所在的训练语料,例如:科学领域、体育领域)中的区分度,它认为越少出现的词区分度越高,相应的 IDF 值也越高。


IDF = \log\dfrac{(\text{训练语料的总文档数+1})}{(\text{出现该词的文档数+1})}

分母 “+1” 是为了防止分母为零,同时相应地分子 “+1” 以防止 IDF 为负数。

算法总结

经验

从计算公式可知,TF-IDF 的准确性非常依赖 IDF 模型的质量。同一个词在不同领域对应的区分度(IDF)也不同甚至差别巨大,例如 “冠军” 在体育领域的语料中出现的次数远远大于在政治领域的语料中所出现的次数,这意味着在后者的语境下,“冠军” 这个词的区分度更高,IDF 值更大。要提升 TF-IDF 的算法效果一般需要根据业务场合单独训练特定领域的 IDF 模型。

评价

  • TF-IDF 计算复杂度低,适合对效率优先的场景。
  • 由于它只依赖关键词的统计特征进行排序,有时候效果并不理想,比如有时候文本的关键要素不一定是频繁出现的元素。
  • 另外它也缺乏对文本结构的考虑。
转载请注明出处:

© http://hejunhao.me

第 1 页,共 3 页123