Spark ml 特征工程

参考:https://www.jianshu.com/p/e662daa8970a

           https://blog.csdn.net/qq_34531825/article/details/52415838

           https://blog.csdn.net/qq_34531825/article/details/52431264

              https://blog.csdn.net/u013090676/article/details/84191468

 

按照惯例,先附上业界那句名言“数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已”。特征工程是指用一系列工程化的方式从原始数据中筛选出更好的数据特征,以提升模型的训练效果。简而言之,就是为算法提供更友好的输入,以最大化地发挥算法的作用。同时,特征工程虽然是一种技术,但其前提是对数据以及产生这些数据的具体业务场景有比较深入的理解。一般的,python的sklearn、spark的mllib都包含了特征工程相关的方法,这里介绍spark的mllib中特征工程相关的方法,但具体实现请参考官方文档。

spark中特征工程在mllib 中Extracting, transforming and selecting features里,其分为Feature Extractors(特征提取)、Feature Transformers(特征转换)、Feature Selectors(特征选择)以及Locality Sensitive Hashing(即LSH局部敏感哈希)。其中特征提取是从原始数据中提取特征,特征转换是从已有的特征中构建更有用的特征,特征选择是从原特征中选择最有效的的一组子特征。局部敏感哈希是一种用于海量高维数据的近似最近邻快速查找技术,本博客不做介绍。

 

 特征提取

spark中特征提取中包含四个方法:TF-IDF、Word2Vec、CountVectorizer以及FeatureHasher。其中,TF-IDF以及Word2Vec的使用比较广泛,这里不详细展开,TF-IDF主要用于提取文档的关键词,而Word2Vec将词语或者文章转换为词向量,通过空间距离表示文档的相似度,距离越近则越相似,其中一篇文章的词向量是文章所有词语词向量的平均值,所以使用Word2Vec尽量使用关键词转换词向量。CountVectorizer与TF相似,输出词频向量,但是CountVectorizer是可逆的,而TF是不可逆的,也就是说,CountVectorizer可以通过词频向量的索引找到对应的单词,而TF则不可以。所以在使用spark做关键词提取时,通常使用CountVectorizer和IDF,而如果只需要文档关键词的特征向量的话,则使用TF和IDF。详细地,可以参考博主spark关键词提取相关的博客:

       https://blog.csdn.net/u013090676/article/details/81952235

最后FeatureHasher即特征哈希,是将原来的N维特征转换为一个M维的特征向量,一般的M小于N。并且原始的N维特征可以是不同的数据类型,比如数字、字符串、布尔值等,对于不同的数据类型,FeatureHasher会使用不同的方式进行处理。

 

  特征转换

特征转换是spark特征工程的主要部分,其中也包含了最多的方法,这里不一一介绍,总的来说,可以分为以下几类:

文档转换
文档转换主要对文档内容数据进行转换。主要有Tokenizer、StopWordsRemover以及n-gram。其中Tokenizer用于分词,StopWordsRemover用于去掉停用词,但是这两个方法针对的是英文,如果要做中文分词以及去掉停用词的话,需要使用中文分词相关的方法,比如ansj。n-gram并不是n-gram算法,而是将分词好的字符串根据参数n的值转换成每组n个字符串的向量。例如,输入["I", "love", "Java"]经过n=2的转换后,输出["I love", "love java"]。

正则化
正则化将每个样本缩放到单位范数上,对于二次型(点积)或者其他核函数方法计算两个样本之间的相似性比较有用。spark中正则化使用的方法是Normalizer,Normalizer作用于每一行(所有特征),将一个行向量的范数转换为单位范数。需要参数p确定是p范数,默认p=2,就是模长(到原点的欧式距离)。特殊的,0范数为绝对值之和,无穷范数是向量的最大值。

标准化
 标准化是在不改变原数据分布的前提下,将数据按比例缩放,使之落入一个限定的区间,使数据之间具有可比性。但当特征明显不遵从高斯正态分布时,标准化的效果比较差。简而言之,就是让不同量纲单位的数据具有可比性。spark中标准化使用StandardScaler。StandardScaler作用于每一列(每一个特征),将特征标准化为单位标准差或是0均值,或是0均值单位标准差。有两个参数:withStd默认为真,转换为单位标准差,withMean默认为假,为真时转为0均值。

归一化
归一化是对数据集进行区间缩放,缩放到单位区间内,把有单位的数据转化为没有单位的数据,即统一数据的衡量标准,消除单位的影响。spark中归一化的方法有MinMaxScaler与MaxAbsScaler,他们都作用于每一列。其中MinMaxScaler将每一维特征映射到[min,max]的区间,通常情况是[0,1]。而MaxAbsScaler通过除以每一维特征最大值的绝对值,将每一维特征映射到[-1,1]上。

二值化
二值化是将数值型特征转换为布尔类型特征,通过设定一个阈值,当特征值大于该阈值时为1 ,小于等于则为0。spark中二值化采用Binarizer。

离散化
离散化即是将连续变量进行离散化,一则部门算法要求输入为离散变量,第二对离散变量的处理效率更高,噪声的影响越低。Spark中离散化的方法有Bucketizer与QuantileDiscretize。其中Bucketizer是讲连续的数据放入已经设置好的n个桶中(即每个桶的上下边界人工设置好),而QuantileDiscretize根据设置的桶的数量对连续变量进行拆分,且它可以处理NaN值。另外,通常也可以使用聚类的方法来对连续变量进行离散化。

独热编码
独热编码(也可称为哑变量)将分类转换为二进制向量,该向量中只有一位为1,其他为0。独热编码一般在需要计算空间距离的算法中对原始的无先后顺序的分类数据进行处理。例如一般的系统中,通过1、2、3对红灯、绿灯和黄灯进行标识,但是红黄绿三种状态本来没有大小和顺序,如果不做处理,进行距离计算时,就会出现绿灯离红灯比黄灯离红灯更近的情况。所以通过独热编码将其转换为红灯[1,0,0]、黄灯[0,1,0]、绿灯[0,0,1],这样三个分类的空间距离也就相同了。独热编码使用OneHotEncoderEstimator,作用于列,可以作用于多个列,对一个特征的值进行独热编码。

缺失值补全
缺失值补全使用Imputer方法。Imputer作用于列,对列中的NaN使用中位数或者平均数进行补全。

特征构造
特征构造通过对已有特征进行拆分、组合构建出新的特征。特征构建的过程,需要对原始数据以及业务特征有一定的理解,例如在销量分析中,通常会把时间拆分成月份和日期,来分析哪些月份、每个月哪几天销量较高。Spark中特征构造的方法包括:PolynomialExpansion、Interaction、ElementwiseProduct和VectorAssembler。其中,PolynomialExpansion将特征进行多项式展开形成新的特征;Interaction将多列向量构造成一个向量,新向量的内容是每个输入列的每个值与其他列组合的乘机;ElementwiseProduct通过将向量列与一个标量列进行乘积来对向量进行缩放;VectorAssembler对输入列进行拼接生成一个新的向量。具体地,可以运行相关示例进行详细的了解。

降维
降维spark中目前博主只找到了PCA。但是一般情况下,数据降维有线性方法与非线性方法。线性方法比较常用的有PCA(主成分分析)与LDA(线性判别式 Linear Discriminant Analysis,注意与Latent Dirichlet Allocation区分开,在spark聚类中,它也叫LDA)。这里不对LDA与PCA进行详细的介绍,只是在应用中需要注意,PCA是为了去掉冗余的维度,使得降低了维度的数据之间的方差最大;而LDA则是为了降维的结果数据更容易分类。

 

特征选择

Spark中特征选择提供了三种方法:VectorSlicer、RFormula以及ChiSqSelector。其中VectorSlicer支持用户通过索引筛选某一个向量列中特定的下标的元素组成新的向量,同时也支持通过名称来筛选,但是比较麻烦;RFormula基于R模型,根据R公式生存标签列和特征列(和博主一样不熟悉R的同学就不用纠结这个了);ChiSqSelector通过卡方独立性检验来选择特征,最终选择与标签列分布最接近(也就是最相关)的特征。因此ChiSqSelector一般用于在特征比较多的时候,用来选取预测能力比较强的特征。

 

 

Extracting, transforming and selecting features

这一大章节讲的内容主要是与特征工程相关的算法,粗略的可以分为如下几类:

  1. Extraction:从Raw数据中提取出特征
  2. Transformation:Scaling, converting, or modifying features
  3. Selection:从大的特征集合中挑选一个子集
  4. Locality Sensitive Hashing (LSH):这类算法将特征变换的方面与其他算法相结合。

Feature Extractors

TF-IDF

TF-IDF是Term frequency(词频)-inverse document frequency(逆文本频率指数)的缩写。是一种在文本挖掘中广泛使用的特征向量化方法,以反映一个单词在语料库中的重要性。定义:t 表示由一个单词,d 表示一个文档,D 表示语料库(corpus),词频 TF(t,d) 表示某一个给定的单词 t 出现在文档 d 中的次数(单词次数), 而文档频率 DF(t,D) 表示包含单词 t文档次数。如果我们只使用词频 TF 来衡量重要性,则很容易过分强调出现频率过高并且文档包含少许信息的单词,例如,'a','the',和 'of'。如果一个单词在整个语料库中出现的非常频繁,这意味着它并没有携带特定文档的某些特殊信息(换句话说,该单词对整个文档的重要程度低)。逆向文档频率是一个数字量度,表示一个单词提供了多少信息:


其中,|D| 是在语料库中文档总数。由于使用对数,所以如果一个单词出现在所有的文件,其IDF值变为0。注意,应用平滑项以避免在语料库之外的项除以零(为了防止分母为0,分母需要加1)。因此,TF-IDF测量只是TF和IDF的产物:(对TF-IDF定义为TF和IDF的乘积)


关于词频TF和文档频率DF的定义有多种形式。在MLlib,我们分离TF和IDF,使其灵活。下面是MLlib中的使用情况简单说明:

 

TFHashingTFCountVectorizer都可以用于生成词频TF向量。
其中HashingTF是一个需要特征词集的转换器(Transformer),它可以将这些集合转换成固定长度的特征向量。CountVectorizer将文本文档转换为关键词计数的向量。

IDF:IDF是一个适合数据集并生成IDFModel的评估器(Estimator),IDFModel获取特征向量(通常由HashingTFCountVectorizer创建)并缩放每列。直观地说,它下调了在语料库中频繁出现的列。

代码示例(Java版)

//创建Row的数据list
List<Row> data = Arrays.asList(
  RowFactory.create(0.0, "Hi I heard about Spark"),
  RowFactory.create(0.0, "I wish Java could use case classes"),
  RowFactory.create(1.0, "Logistic regression models are neat")
);
//创建Schema给Row定义数据类型和fieldName
StructType schema = new StructType(new StructField[]{
  new StructField("label", DataTypes.DoubleType, false, Metadata.empty()),
  new StructField("sentence", DataTypes.StringType, false, Metadata.empty())
});
Dataset<Row> sentenceData = spark.createDataFrame(data, schema);

//使用Tokenizer来将句子分割成单词
Tokenizer tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words");
Dataset<Row> wordsData = tokenizer.transform(sentenceData);

//使用HashingTF将句子中的单词哈希成特征向量(这个可以在前一章节的最后输出打印截图中看到具体的值)
int numFeatures = 20;
HashingTF hashingTF = new HashingTF()
  .setInputCol("words")
  .setOutputCol("rawFeatures")
  .setNumFeatures(numFeatures);

Dataset<Row> featurizedData = hashingTF.transform(wordsData);
// alternatively, CountVectorizer can also be used to get term frequency vectors

//使用IDF对上面产生的特征向量进行rescale
IDF idf = new IDF().setInputCol("rawFeatures").setOutputCol("features");
IDFModel idfModel = idf.fit(featurizedData); //fit得到IDF的模型

Dataset<Row> rescaledData = idfModel.transform(featurizedData); //对特征向量进行rescale
rescaledData.select("label", "features").show();

//最后得到的特征向量可以作为其他机器学习算法的输入

rescaledData.select("label", "words", "rawFeatures", "features").show()

Word2Vec

Word2Vec是一个Estimator(评估器),它采用表示文档的单词序列,并训练一个Word2VecModel。 该模型将每个单词映射到一个唯一的固定大小向量。 Word2VecModel使用文档中所有单词的平均值将每个文档转换为向量; 该向量然后可用作预测,文档相似性计算等功能。有关更多详细信息,请参阅有关Word2Vec的MLlib用户指南
代码示例(Java版)

public class JavaWord2VecExample {
  public static void main(String[] args) {
    SparkSession spark = SparkSession
      .builder()
      .master("local[4]")
      .appName("JavaWord2VecExample")
      .getOrCreate();

    // $example on$
    // Input data: Each row is a bag of words from a sentence or document.
    List<Row> data = Arrays.asList(
      RowFactory.create(Arrays.asList("Hi I heard about Spark".split(" "))),
      RowFactory.create(Arrays.asList("I wish Java could use case classes".split(" "))),
      RowFactory.create(Arrays.asList("Logistic regression models are neat".split(" ")))
    );
    StructType schema = new StructType(new StructField[]{
      new StructField("text", new ArrayType(DataTypes.StringType, true), false, Metadata.empty())
    });
    Dataset<Row> documentDF = spark.createDataFrame(data, schema);

    //创建Word2Vec的实例,然后设置参数
    // Learn a mapping from words to Vectors.
    Word2Vec word2Vec = new Word2Vec()
      .setInputCol("text")
      .setOutputCol("result")
      .setVectorSize(3)
      .setMinCount(0);

    Word2VecModel model = word2Vec.fit(documentDF); //fit出模型
    Dataset<Row> result = model.transform(documentDF); //对输入进行transform得到结果DataFrame

    for (Row row : result.collectAsList()) {
      List<String> text = row.getList(0);
      Vector vector = (Vector) row.get(1);
      System.out.println("\n\nText: " + text + " => \nVector: " + vector + "\n\n\n");
    }
    // $example off$

    spark.stop();
  }
}

Word2Vec示例运行结果


Feature Transformers

下图是对特征转换的API doc中列出的算法。不过这里不打算把每个都展开描述了,会简单举例几个,然后后面实际用到哪个再去查哪个。

API Doc中特征转换算法列表

Tokenizer

Tokenization(文本符号化)是将文本 (如一个句子)拆分成单词的过程。(在Spark ML中)Tokenizer(分词器)提供此功能。下面的示例演示如何将句子拆分为词的序列。
RegexTokenizer 提供了(更高级的)基于正则表达式 (regex) 匹配的(对句子或文本的)单词拆分。默认情况下,参数"pattern"(默认的正则表达式: "\\s+"
,此时和Tokenizer没有区别
) 作为分隔符用于拆分输入的文本。或者,用户可以将参数“gaps”设置为 false(不然默认true的情况下分割出来的结果是分隔符的集合,而不是单词的集合),指定正则表达式"pattern"表示"tokens",而不是分隔符,这样作为划分结果找到的所有匹配项

回顾下正则表达式

代码示例(Java版)

public class JavaTokenizerExample {
  public static void main(String[] args) {
    SparkSession spark = SparkSession
      .builder()
      .appName("JavaTokenizerExample")
      .getOrCreate();

    //构造输入数据,注意第三行的数据word之间只有逗号没有空格
    // $example on$
    List<Row> data = Arrays.asList(
      RowFactory.create(0, "Hi I heard about Spark"),
      RowFactory.create(1, "I wish Java could use case classes"),
      RowFactory.create(2, "Logistic,regression,models,are,neat")
    );

    StructType schema = new StructType(new StructField[]{
      new StructField("id", DataTypes.IntegerType, false, Metadata.empty()),
      new StructField("sentence", DataTypes.StringType, false, Metadata.empty())
    });

    Dataset<Row> sentenceDataFrame = spark.createDataFrame(data, schema);

    //Tokenizer划分单词是按照空格来做的
    Tokenizer tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words");

    //通过setPattern来让正则表达的划分按照非字母来做
    RegexTokenizer regexTokenizer = new RegexTokenizer()
        .setInputCol("sentence")
        .setOutputCol("words")
        .setPattern("\\W");  // alternatively .setPattern("\\w+").setGaps(false); 换成这句话一样的结果。

    //注册一个user-defined functions,第一个参数是udf的名字,第二个参数是一个自定义的转换函数。
    spark.udf().register("countTokens", new UDF1<WrappedArray, Integer>() {
      @Override
      public Integer call(WrappedArray words) {
        return words.size();
      }
    }, DataTypes.IntegerType);

    //按照空格划分,结果第三行没有划分,当作整体对待。
    Dataset<Row> tokenized = tokenizer.transform(sentenceDataFrame);
    tokenized.select("sentence", "words")
        .withColumn("tokens", callUDF("countTokens", col("words"))).show(false);

    //按照正则表达式对待,把非字母的地方划分了。
    Dataset<Row> regexTokenized = regexTokenizer.transform(sentenceDataFrame);
    regexTokenized.select("sentence", "words")
        .withColumn("tokens", callUDF("countTokens", col("words"))).show(false);
    // $example off$

    spark.stop();
  }
}

两种Tokenizer

VectorAssembler(特征向量合并)

【这个API超级有用!】VectorAssembler 是将指定的一list的列合并到单个列向量中的 transformer。它可以将原始特征和不同特征transformers(转换器)生成的特征合并为单个特征向量,来训练 ML 模型,如逻辑回归和决策树等机器学习算法。VectorAssembler 可接受以下的输入列类型:任何数值型布尔类型向量类型。输入列的值将按指定顺序依次添加到一个向量中。

举例
假设现在有一个DataFrame,它的列为:id, hour, mobile, userFeatures和clicked:

idhourmobileuserFeaturesclicked
0181.0[0.0, 10.0, 0.5]1.0

其中userFeatures是一个列向量包含三个用户特征。现在想把hour, mobile, 和userFeatures合并到一个一个特征向量中(名为features,这个也是很多MLlib算法默认的特征输入向量名字),然后用来预测clicked的值。具体用法就是将列hour、mobile和userFeatures作为input,features作为output,然后调用transform之后得到新的DataFrame:

idhourmobileuserFeaturesclickedfeatures
0181.0[0.0, 10.0, 0.5]1.0[18.0, 1.0, 0.0, 10.0, 0.5]

示例Java代码:

public class JavaVectorAssemblerExample {
  public static void main(String[] args) {
    SparkSession spark = SparkSession
      .builder()
      .appName("JavaVectorAssemblerExample")
      .getOrCreate();

    // $example on$
    StructType schema = createStructType(new StructField[]{
      createStructField("id", IntegerType, false),
      createStructField("hour", IntegerType, false),
      createStructField("mobile", DoubleType, false),
      createStructField("userFeatures", new VectorUDT(), false),
      createStructField("clicked", DoubleType, false)
    });
    Row row = RowFactory.create(0, 18, 1.0, Vectors.dense(0.0, 10.0, 0.5), 1.0);
    Dataset<Row> dataset = spark.createDataFrame(Arrays.asList(row), schema);
    System.out.println("\n-------Before assembled the original is:");
    dataset.show(false);

    VectorAssembler assembler = new VectorAssembler()
      .setInputCols(new String[]{"hour", "mobile", "userFeatures"})
      .setOutputCol("features");

    Dataset<Row> output = assembler.transform(dataset);
    System.out.println("\n+++++++Assembled columns 'hour', 'mobile', 'userFeatures' to vector column " +
        "'features'");
    output.select("features", "clicked").show(false);
    // $example off$

    spark.stop();
  }
}

运行结果:

-------Before assembled the original is:
+---+----+------+--------------+-------+
|id |hour|mobile|userFeatures  |clicked|
+---+----+------+--------------+-------+
|0  |18  |1.0   |[0.0,10.0,0.5]|1.0    |
+---+----+------+--------------+-------+


+++++++Assembled columns 'hour', 'mobile', 'userFeatures' to vector column 'features'
+-----------------------+-------+
|features               |clicked|
+-----------------------+-------+
|[18.0,1.0,0.0,10.0,0.5]|1.0    |
+-----------------------+-------+


Feature Selectors

下面只介绍几种MLlib提供的特特征选择算法,其余参见API Doc,后续如果自己用到会再补充。

VectorSlicer(向量切片机)

VectorSlicer是一个转换器,它对于输入的特征向量,输出一个新的原始特征子集的特征向量。对于从列向量中提取特征很有帮助。
VectorSlicer对于指定索引的列向量,输出一个新的列向量,所选择的列向量通过这些索引进行选择。有两种类型的索引:

  • 整数索引:代表列向量的下标,setIndices()
  • 字符串索引:代表列的特征名称,setNames()。这要求列向量有AttributeGroup,因为实现中是在Attribute上的name字段匹配。

整数和字符串的索引都可以接受。此外,还可以同时使用整数索引和字符串名称索引。但必须至少选择一个特征。重复的特征选择是不允许的,所以选择的索引和名称之间不能有重叠。请注意,如果选择了特征的名称索引,则遇到空的输入属性时会抛出异常。
输出时将按照选择中给出的特征索引的先后顺序进行向量及其名称的输出。
举例:
假设有一个DataFrame它的列名(AttributeGroup)为userFeatures

userFeatures
[0.0, 10.0, 0.5]

userFeatures是一个包含三个用户特征的列向量。假设userFeature的第一列全部为0,因此我们要删除它并仅选择最后两列。VectorSlicer使用setIndices(1,2)选择最后两个元素,然后生成一个名为features的新向量列:

userFeaturesfeatures
[0.0, 10.0, 0.5]10.0, 0.5

如果userFeatures已经输入了属性值["f1", "f2", "f3"],那么我们可以使用setNames("f2", "f3")来选择它们。

userFeaturesfeatures
[0.0, 10.0, 0.5]10.0, 0.5
["f1", "f2", "f3"]["f2", "f2"]

下面是一个Java代码示例:

public class JavaVectorSlicerExample {
  public static void main(String[] args) {
    SparkSession spark = SparkSession
      .builder()
      .appName("JavaVectorSlicerExample")
      .getOrCreate();

    //构造AttributeGroup来方便vectorSlicer使用setNames
    // $example on$
    Attribute[] attrs = new Attribute[]{
      NumericAttribute.defaultAttr().withName("f1"),
      NumericAttribute.defaultAttr().withName("f2"),
      NumericAttribute.defaultAttr().withName("f3")
    };
    AttributeGroup group = new AttributeGroup("userFeatures", attrs);

    //构造数据
    List<Row> data = Lists.newArrayList(
      RowFactory.create(Vectors.sparse(3, new int[]{0, 1}, new double[]{-2.0, 2.3}).toDense()), //这里必须使用toDense()来避免sprse的数据结构引起下面的切片时的问题。
      RowFactory.create(Vectors.dense(-2.0, 2.3, 0.0)) //dense和sparse的区别在与sparse是稀疏的适合大量0数据的构造,dense是把每个数值都要赋值的适合非稀疏的情况。
    );

    Dataset<Row> dataset =
      spark.createDataFrame(data, (new StructType()).add(group.toStructField()));
    System.out.println("\n=======Original DataFrame is:");
    dataset.show(false);

    //构造VectorSlicer设置输入列为"userFeatures",输出列为“features”
    VectorSlicer vectorSlicer = new VectorSlicer()
      .setInputCol("userFeatures").setOutputCol("features");

    //setIndices和setNames来选择int[]和String[]的特征列
    vectorSlicer.setIndices(new int[]{1}).setNames(new String[]{"f3"});
    // or slicer.setIndices(new int[]{1, 2}), or slicer.setNames(new String[]{"f2", "f3"})

    Dataset<Row> output = vectorSlicer.transform(dataset);
    System.out.println("\n---------After slice select the output DataFrame is:");
    output.show(false);
    // $example off$

    spark.stop();
  }
}

VectorSlicer的输出结果

如果对于sparse向量不使用toDense方法那么结果就是对sparse结构的数据进行slice操作,结果如下:

 

错误的VectorSlicer结果,目前我已将这个小bug在github上提交了pull request


Locality Sensitive Hashing

LSH是哈希技术中重要的一种,通常用于集群,近似最近邻搜索和大型数据集的孤立点检测。
LSH的大致思路是用一系列函数(LSH families)将数据哈希到桶中,这样彼此接近的数据点处于相同的桶中可能性就会很高,而彼此相距很远的数据点很可能处于不同的桶中。一个LSH family 正式定义如下。
在度量空间(M,d)中,M是一个集合,d是M上的一个距离函数,LSH family是一系列能满足以下属性的函数h


满足以上条件的LSH family被称为(r1, r2, p1, p2)-sensitive。
在Spark中,不同的LSH families实现在不同的类中(例如:MinHash),并且在每个类中提供了用于特征变换的API,近似相似性连接和近似最近邻。
在LSH中,我们将一个假阳性定义为一对相距大的输入特征(当 d(p,q)≥r2 时),它们被哈希到同一个桶中,并且将一个假阴性定义为一对相邻的特征(当 d(p,q)≤r1 时 ),它们被分散到不同的桶中。

 

LSH Operations(LSH运算)

我们描述了大部分LSH会用到的运算,每一个合适的LSH模型都有自己的方法实现了这些运算。

Feature Transformation(特征变换)

特征变换是将哈希值添加为新列的基本功能。 这可以有助于降低维数。 用户可以通过设置 inputCol 和 outputCol 参数来指定输入和输出列名。
LSH 还支持多个LSH哈希表。 用户可以通过设置 numHashTables 来指定哈希表的数量。 这也用于近似相似性连接和近似最近邻的 OR-amplification(或放大器)放大。 增加哈希表的数量将增加准确性,但也会增加通信成本和运行时间。
outputCol 的类型是 Seq [Vector],其中数组的维数等于 numHashTables ,并且向量的维度当前设置为1。在将来的版本中,我们将实现 AND-amplification(与放大器),以便用户可以指定这些向量的维度 。

Approximate Similarity Join(近似相似度连接)

近似相似度连接采用两个数据集,并且近似返回距离小于用户定义阈值的数据集中的行对。 近似相似度连接支持两个不同的数据集连接和自连接。 Self-joinin (自连接)会产生一些重复的对。
近似相似度连接接受已转换和未转换的数据集作为输入。 如果使用未转换的数据集,它将自动转换。 在这种情况下,哈希签名将被创建为outputCol。
在加入的数据集中,可以在数据集A和数据集B中查询原始数据集。 距离列将被添加到输出数据集,以显示返回的每对行之间的真实距离。

Approximate Nearest Neighbor Search(近似最邻近搜索)

近似最近邻搜索采用数据集(特征向量)和Key键(单个特征向量),并且它近似返回数据集中最接近向量的指定数量的行。
近似最近邻搜索接受已转换和未转换的数据集作为输入。 如果使用未转换的数据集,它将自动转换。 在这种情况下,哈希签名将被创建为outputCol。
距离列将被添加到输出数据集,以显示每个输出行和搜索的键之间的真实距离。
注意:当哈希桶中没有足够的候选项时,近似最近邻搜索将返回少于k行。

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值