Apache spark 如何使用Spark查找10亿条记录的最近邻?

Apache spark 如何使用Spark查找10亿条记录的最近邻?,apache-spark,pyspark,spark-dataframe,nearest-neighbor,euclidean-distance,Apache Spark,Pyspark,Spark Dataframe,Nearest Neighbor,Euclidean Distance,鉴于10亿条记录包含以下信息: ID x1 x2 x3 ... x100 1 0.1 0.12 1.3 ... -2.00 2 -1 1.2 2 ... 3 ... 对于上面的每个ID,我想根据向量的欧几里德距离(x1,x2,…,x100)找到前10个最接近的ID 计算这个问题的最佳方法是什么?您没有提供太多细节,但我解决这个问题的一般方法是: 将记录转换为数据结构,如以(ID,x1..x100)为标签和特征的 映射每个记录,

鉴于10亿条记录包含以下信息:

    ID  x1  x2  x3  ... x100
    1   0.1  0.12  1.3  ... -2.00
    2   -1   1.2    2   ... 3
    ...
对于上面的每个ID,我想根据向量的欧几里德距离(x1,x2,…,x100)找到前10个最接近的ID


计算这个问题的最佳方法是什么?

您没有提供太多细节,但我解决这个问题的一般方法是:

  • 将记录转换为数据结构,如以(ID,x1..x100)为标签和特征的
  • 映射每个记录,并将该记录与所有其他记录进行比较(此处有很大的优化空间)
  • 创建一些切断逻辑,以便在开始比较ID=5和ID=1时中断计算,因为您已经比较了ID=1和ID=5
  • 一些减少步骤以获得像
    {id\u pair:[1,5],distance:123}
  • 另一个映射步骤是查找每个记录的10个最近邻居
  • 您已经识别了pyspark,我通常使用scala进行这类工作,但每个步骤的一些伪代码可能如下所示:

    # 1. vectorize the features
    def vectorize_raw_data(record)
        arr_of_features = record[1..99]
        LabeledPoint( record[0] , arr_of_features)
    
    # 2,3 + 4 map over each record for comparison
    broadcast_var = [] 
    def calc_distance(record, comparison)
        # here you want to keep a broadcast variable with a list or dictionary of
        # already compared IDs and break if the key pair already exists
        # then, calc the euclidean distance by mapping over the features of
        # the record and subtracting the values then squaring the result, keeping 
        # a running sum of those squares and square rooting that sum
        return {"id_pair" : [1,5], "distance" : 123}    
    
    for record in allRecords:
      for comparison in allRecords:
        broadcast_var.append( calc_distance(record, comparison) )
    
    # 5. map for 10 closest neighbors
    
    def closest_neighbors(record, n=10)
         broadcast_var.filter(x => x.id_pair.include?(record.id) ).takeOrdered(n, distance)
    

    伪代码很糟糕,但我认为它传达了意图。当您将所有记录与所有其他记录进行比较时,这里将有大量的洗牌和排序。总之,您希望将密钥对/距离存储在一个中心位置(就像一个广播变量,虽然这很危险,但会被更新),以减少您执行的欧几里德距离计算总量。

    对所有记录与所有记录进行蛮力比较是一场失败的战斗。我的建议是使用k-最近邻算法的现成实现,如
    scikit learn
    提供的算法,然后广播结果的索引和距离数组,然后再进一步

    这种情况下的步骤是:

    1-按照Bryce的建议对功能进行矢量化,并让您的矢量化方法返回一个浮动列表(或numpy数组),其中包含与功能相同数量的元素

    2-将您的scikit learn nn与您的数据相匹配:

    nbrs = NearestNeighbors(n_neighbors=10, algorithm='auto').fit(vectorized_data)
    
    3-在矢量化数据上运行经过训练的算法(在您的案例中,训练和查询数据是相同的)

    步骤2和3将在pyspark节点上运行,在这种情况下不可并行化。此节点上需要有足够的内存。在我这个拥有150万条记录和4项功能的例子中,花了一两秒钟


    在我们为spark实现NN之前,我想我们必须坚持这些变通方法。如果你想尝试一些新的东西,那么就选择它,我有一个解决方案,包括将sklearn与Spark结合起来:

    其要点是:

    • 集中使用sklearn的k-NN fit()方法
    • 然后分布式地使用sklearn的k-NN kneighbors()方法

    您尝试过什么?我们要求您向我们展示您迄今为止所做的尝试,当您遇到问题或不理解错误,并且文档无法提供帮助时,我们将在这里尝试。此外,重要的是要包含一些示例数据,以便其他用户可以轻松地复制和粘贴到他们自己的环境中,这样他们就可以在自己的环境中进行操作。实际上,您的答案中的第3步是可并行的:sklearn的k-NN kneighbors()方法可以与Spark一起分发!我已经在这里发布了方法:谢谢你的提醒!
    distances, indices = nbrs.kneighbors(qpa)