Spark数据挖掘-基于 LSA 隐层语义分析理解APP描述信息(2) - clebeg的个人空间 - 开源中国

标签: | 发表时间:2018-08-11 18:55 | 作者:
出处:https://my.oschina.net


Spark数据挖掘-基于 LSA 隐层语义分析理解APP描述信息(2)


1 前言

Spark 通过调用 RowMatrix 的 computeSVD 方法会得到三个重要的矩阵 U、S、V , 而且:原始矩阵 近似等于 U * S * V
它们含义分别如下:

  • V: 每一行表示单词,列表示概念,矩阵的值表示单词在概念里面的重要程度
  • U: 每一行表示文档,列表示概念,矩阵的值表示文档在概念里面的重要程度
  • S: 对角矩阵,每一个对角线元素代表概念的重要程度

通过这个文档,首先想到的是文档中最重要的概念是什么?概念往往对应话题,这样基本就能确定文档的主题了,然后每个主题通过V矩阵可以得到重要的词,这样就可以给文档添加标签了,但是其实可以走的更远,本文将重点研究如何使用这两个矩阵,这里的用途很容易推广到LDA模型,LDA 模型得到 phi(词与topic关系矩阵) 和 theta(文档与topic的关系矩阵) 两个矩阵之后也可以干这些事。接下来主要尝试回答下面三个问题:

  • 文档与文档关系如何?
  • 词与词关系如何?
  • 词与文档关系如何?
  • 给出一系列查询词最相关的文档是哪些?

2 粗浅的解决方案

其实从最原始的词文档矩阵可以得到上面这些问题粗浅的答案:比如词与词的重要程度可以计算词文档矩阵中对应列之间的余弦相似性。余弦相似度计算的是高维文档空间中两个点之间向量的夹角,越相同方向的点认为相似度越高。余弦相似度计算的就是两个向量的点积除以两个向量长度的乘积。通用的计算行之间的余弦距离就得到了文档与文档的相似度。而词与文档的重要程度就对应该矩阵中这两个对象交叉的位置。
然而这些重要性得分都是很粗浅的,因为这些都是来自于对文档中词简单计数统计,根本没有考虑词之间的语义关系等。

3 基于LSA的解决方案

LSA 提供更深入的理解语料库的得分矩阵。基于这些矩阵可以得到更加有深度的结论。例如:一些文件只出现“新闻”,但是不出现“资讯”,而另外一下文章刚好相反,但是它们可能会通过“阅读”这个词联系在一起。
LSA 这种表示的方式从效率的角度来看也更有优势。LSA 将重要的信息压缩到低维空间来代替原始的词文档矩阵。这样使得很多计算更加快。因为计算原始词文档矩阵中词与其他词的重要程度需要的时间复杂度正比于单词的数量乘以文档的数量。LSA 可以通过将概念空间表示映射到词空间达到通用的效果,时间复杂度正比于单词的数量乘以概念的个数 K。数据之间的相关性通过这种低秩近似重新编码从而使得不需要访问整个语料库。

4 词-词相关性

LSA 是如何理解词与词之间的距离的呢?其实通过将 SVD 奇异值分解得到的三个矩阵相乘就会得到原始矩阵的一个近似,那么这个近似矩阵列之间的余弦距离就是原始的词距离的一个近似,只不过现在有下面的三点优化:

  • 同义词变为同一个词
  • 降低一词多义词的权重
  • 去掉一些噪音

这样就会使得词之间的距离更加合理。幸运的是不需要重新将三个因子矩阵相乘再去计算词之间的相似性,线性代数已经证明:相乘之后的矩阵列与列之间的余弦就等于 St(V):(t 表示转置)这个矩阵对应列之间的余弦。
考虑给定一个词计算最相关的特定词这个任务:由于S是对角矩阵,那么S的转置就等于S,那么S*t(V)的列就变为了VS的行。通过对VS的每一行长度归一化,然后将VS乘以给定词对应的列转变的列向量就得到了这个词与每个词之间的余弦。具体代码如下:

      import breeze.linalg.{DenseVector => BDenseVector}
import breeze.linalg.{DenseMatrix => BDenseMatrix}
def topTermsForTerm(
  normalizedVS: BDenseMatrix[Double],
  termId: Int): Seq[(Double, Int)] = {
  //得到termId对应的行
  val rowVec = new BDenseVector[Double](
  row(normalizedVS, termId).toArray)
  //将VS归一化的矩阵乘上面对应的行,得到该term与每个单词的余弦距离
  val termScores = (normalizedVS * rowVec).toArray.zipWithIndex
  termScores.sortBy(-_._1).take(10)
}
//计算 VS
val VS = multiplyByDiagonalMatrix(svd.V, svd.s)
//将 VS 的行归一化
val normalizedVS = rowsNormalized(VS)
def printRelevantTerms(term: String) {
  val id = idTerms(term)
  printIdWeights(topTermsForTerm(normalizedVS, id, termIds)
}

利用上面的代码查询了和“银行”相关的词,结果如下:

      银行:1.0000000000000007
农商:0.5731472845623417
浦发:0.5582996267582955
灵犀:0.5546113928156614
乌海:0.5181220508630512
邮政:0.49403542009285284
花旗:0.4767076670441433
渣打:0.4646580481689233
通畅:0.46282711600593196
缝隙:0.4500830196782121

语料库不足是导致效果一般的最大问题。

5 文档-文档相关性

文档与文档的相关性与词与词之间的相关性思路完全一样,只不过这次用的矩阵是U,U是分布式存储的,所以代码有点不同:

      import org.apache.spark.mllib.linalg.Matrices
def topDocsForDoc(normalizedUS: RowMatrix, docId: Long)
  : Seq[(Double, Long)] = {
  val docRowArr = row(normalizedUS, docId)
  val docRowVec = Matrices.dense(docRowArr.length, 1, docRowArr)
  val docScores = normalizedUS.multiply(docRowVec)
  val allDocWeights = docScores.rows.map(_.toArray(0)).
  zipWithUniqueId()
  allDocWeights.filter(!_._1.isNaN).top(10)
}
val US = multiplyByDiagonalMatrix(svd.U, svd.s)
val normalizedUS = rowsNormalized(US)
def printRelevantDocs(doc: String) {
  val id = idDocs(doc)
  printIdWeights(topDocsForDoc(normalizedUS, id, docIds)
}

6 词-文档之间的相关性

同样的道理,词文档之间的相关性也是通过 USV 这个矩阵中的每个位置的元素去近似的,比如词 t 与文档 d 的关系就是: U(d) * S * V(t),根据线性代数的基本理论,可以很容易得到词 t 与所有文档的关系为:U * S * V(t) 或者文档 d 与所有词的关系为:U(d) * S * V。这样就很容易知道与某个文档最相关的前几个词,以及与某个词最相关的文档。具体的应用代码如下:

      def topDocsForTerm(US: RowMatrix, V: Matrix, termId: Int)
  : Seq[(Double, Long)] = {
  //得到词对应的行
  val rowArr = row(V, termId).toArray
  //将改行转为列向量
  val rowVec = Matrices.dense(termRowArr.length, 1, termRowArr)
  //计算 US 乘以列向量
  val docScores = US.multiply(termRowVec)
  //得到所有文档与词之间的关系的得分
  val allDocWeights = docScores.rows.map(_.toArray(0)).zipWithUniqueId()
  //选择最重要的10篇文档
  allDocWeights.top(10)
}
//打印结果
def printRelevantDocs(term: String) {
  val id = idTerms(term)
  printIdWeights(topDocsForTerm(normalizedUS, svd.V, id, docIds)
}

7 查询多个词相关的文档

查询一个词,相当于上面的词与文档的关系,其实就是将 V 转置之后乘以一个长度为词向量长而且只有一个元素为 1 的列向量,值为 1 的位置对应的就是该词的位置,而多个词,那么就通过乘以一个长度为词向量长而且对应查询词位置都是该词的idf权重其他位置为0的列向量即可。具体代码如下:

      import breeze.linalg.{SparseVector => BSparseVector}
//得到查询词对应位置为idf权重,其他位置为0的向量
def termsToQueryVector(
  terms: Seq[String],
  idTerms: Map[String, Int],
  idfs: Map[String, Double]): BSparseVector[Double] = {
    //先得到查询词在整个词向量的下标索引位置
    val indices = terms.map(idTerms(_)).toArray
    //将对应位置的idf权重找出来
    val values = terms.map(idfs(_)).toArray
    //转为向量 长度为词向量长度,很多位置的值为零,查询词位置值为 idf 权重
    new BSparseVector[Double](indices, values, idTerms.size)
}
//得到 US*t(t(V)*上面方法得到的向量)
def topDocsForTermQuery(
  US: RowMatrix,
  V: Matrix,
  query: BSparseVector[Double]): Seq[(Double, Long)] = {
    val breezeV = new BDenseMatrix[Double](V.numRows, V.numCols,
    V.toArray)
    //计算 t(V)*上面方法得到的向量
    val termRowArr = (breezeV.t * query).toArray
    //得到 t(t(V)*上面方法得到的向量)
    val termRowVec = Matrices.dense(termRowArr.length, 1, termRowArr)
    //计算 US*t(t(V)*上面方法得到的向量)
    val docScores = US.multiply(termRowVec)
    val allDocWeights = docScores.rows.map(_.toArray(0)).
      zipWithUniqueId()
    allDocWeights.top(10)
}
def printRelevantDocs(terms: Seq[String]) {
  val queryVec = termsToQueryVector(terms, idTerms, idfs)
  printIdWeights(topDocsForTermQuery(US, svd.V, queryVec), docIds)
}

8 附录

上面代码中用到一些辅助的方法,因为比较简单就不详细分析,这里简单做一个汇总:

      def row(normalizedVS: DenseMatrix[Double], termId: Int) = {
  (0 until normalizedVS.cols).map(i => normalizedVS(termId, i))
}

def multiplyByDiagonalMatrix(mat: Matrix, s: Vector) = {
  val sArr = s.toArray
  new BDenseMatrix[Double](mat.numRows, mat.numCols, mat.toArray)
  .mapPairs{case ((r, c), v) => v * sArr(c)}
}

def rowsNormalized(bm: BDenseMatrix[Double]) = {
  val newMat = new BDenseMatrix[Double](bm.rows, bm.cols)
  for (r <- 0 until bm.rows) {
    val len = math.sqrt((0 until bm.cols).map{c => math.pow(bm(r, c), 2)}.sum)
    (0 until bm.cols).foreach{c => newMat.update(r, c, bm(r, c)/len)}
  }
  newMat
}



相关 [spark 数据挖掘 lsa] 推荐:

Spark数据挖掘-基于 LSA 隐层语义分析理解APP描述信息(1) - clebeg的个人空间 - 开源中国

- -
Spark数据挖掘-基于 LSA 隐层语义分析理解APP描述信息(1). 结构化数据处理比较直接,然而非结构化数据(比如:文本、语音)处理就比较具有挑战. 对于文本现在比较成熟的技术是搜索引擎,它可以帮助人们从给定的词语中快速找到包含关键词的文本. 但是,一些情况下人们希望找到某一个概念的文本,而不关心文本里面是否包含某个关键词.

Spark数据挖掘-基于 LSA 隐层语义分析理解APP描述信息(2) - clebeg的个人空间 - 开源中国

- -
Spark数据挖掘-基于 LSA 隐层语义分析理解APP描述信息(2). Spark 通过调用 RowMatrix 的 computeSVD 方法会得到三个重要的矩阵 U、S、V , 而且:原始矩阵 近似等于 U * S * V. V: 每一行表示单词,列表示概念,矩阵的值表示单词在概念里面的重要程度.

Latent Semantic Analysis(LSA) - CSDN博客

- -
Latent Semantic Analysis(LSA)中文翻译为潜语义分析,也被叫做Latent Semantic Indexing ( LSI ). 意思是指通过分析一堆(不止一个)文档去发现这些文档中潜在的意思和概念,什么叫潜在的意思. 我第一次看到这个解释,直接懵逼. 假设每个词仅表示一个概念,并且每个概念仅仅被一个词所描述,LSA将非常简单(从词到概念存在一个简单的映射关系).

数据挖掘是神马?

- - 互联网分析
1、数据挖掘需要‘神马样’的流程.  2、哥,有没有详细点的,来个给力的. 4、数据在统计意义上有哪些类型. 9、知道这些工具不知道如何在工作中用呀. 11、还有没有更人性化、智能化的展现. 12、上面这图看起来很给力,背后很复杂吧.  16、转载的留个来源 ,毕竟是我辛苦收集和想出来的,谢谢. 忘记“大数据”,从“中数据”开始.

这就是数据挖掘

- - 互联网分析
当今数据库的容量已经达到上万亿的水平(T)— 1,000,000,000,000个字节. 在这些大量数据的背后隐藏了很多具有决策意义的信息,那么怎么得到这些“知识”呢. 也就是怎样通过一颗颗的树木了解到整个森林的情况. 计 算机科学对这个问题给出的最新回答就是:数据挖掘,在“数据矿山”中找到蕴藏的“知识金块”,帮助企业减少不必要投资的同时提高资金回报.

关于数据挖掘

- - 牛国柱
以下内容来自网络,关于数据挖掘的一些最基本的知识. 数据挖掘是对一系列数据进行分析和挖掘的方法的统称,在精准营销领域,最常用的数据挖掘方法主要包括以下三类:分类、聚类、关联. 分类(Classify)属于预测性模型. 分类模型的构建需要“训练样本”,训练样本中的每一个个体的类别必须是明确的. 分类模型的特征变量一般称为“自变量”,又叫“预测变量”,类别变量称为“目标变量”.

数据挖掘与Taco Bell编程

- everfly - 译言-每日精品译文推荐
来源Data Mining and Taco Bell Programming. Programmer Ted Dziuba suggests an alternative to traditional program that he called "Taco Bell Programming." The Taco Bell chain creates multiple menu items from about eight different ingredients.

使用Weka进行数据挖掘

- - 搜索研发部官方博客
数据挖掘、机器学习这些字眼,在一些人看来,是门槛很高的东西. 诚然,如果做算法实现甚至算法优化,确实需要很多背景知识. 但事实是,绝大多数数据挖掘工程师,不需要去做算法层面的东西. 他们的精力,集中在特征提取,算法选择和参数调优上. 那么,一个可以方便地提供这些功能的工具,便是十分必要的了. 而weka,便是数据挖掘工具中的佼佼者.

数据挖掘 - 分类算法比较

- - IBM developerWorks 中国 : 文档库
随着计算能力、存储、网络的高速发展,人类积累的数据量正以指数速度增长. 对于这些数据,人们迫切希望从中提取出隐藏其中的有用信息,更需要发现更深层次的规律,对决策,商务应用提供更有效的支持. 为了满足这种需求,数据挖掘技术的得到了长足的发展,而分类在数据挖掘中是一项非常重要的任务,目前在商业上应用最多.

数据挖掘分类技术

- - CSDN博客云计算推荐文章
从分类问题的提出至今,已经衍生出了很多具体的分类技术. 下面主要简单介绍四种最常用的分类技术,不过因为原理和具体的算法实现及优化不是本书的重点,所以我们尽量用应用人员能够理解的语言来表述这些技术. 而且我们会在第4章再次给读者讲述分类算法和相关原理. 在我们学习这些算法之前必须要清楚一点,分类算法不会百分百准确.