次の情報を含む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を見つけたいと思います。
これを計算するための最良の方法は何ですか?
すべてのレコードに対してすべてのレコードのブルートフォース比較を実行することは、敗戦です。私の提案は、scikit-learn
によって提供されるようなk最近傍アルゴリズムの既製の実装を選択し、結果として得られるインデックスと距離の配列をブロードキャストして、さらに先に進むことです。
この場合の手順は次のとおりです。
1-ブライスが提案したように特徴をベクトル化し、ベクトル化メソッドが特徴と同じ数の要素を持つフロートのリスト(またはnumpy配列)を返すようにします
2-scikit-learnnnをデータに適合させます。
nbrs = NearestNeighbors(n_neighbors=10, algorithm='auto').fit(vectorized_data)
3-ベクトル化されたデータに対してトレーニング済みアルゴリズムを実行します(トレーニングデータとクエリデータは同じです)
distances, indices = nbrs.kneighbors(qpa)
手順2と3はpysparkノードで実行され、この場合は並列化できません。このノードには十分なメモリが必要です。 150万件のレコードと4つの機能を備えた私の場合、1〜2秒かかりました。
sparkのNNの適切な実装が得られるまで、これらの回避策に固執する必要があると思います。何か新しいことを試してみたい場合は、 http: //spark-packages.org/package/saurfang/spark-knn
たまたま、sklearnとSparkを組み合わせることで、これに対する解決策があります: https://adventuresindatascience.wordpress.com/2016/04/02/integrating-spark-with-scikit-learn-visualizing-eigenvectors -and-fun /
その要点は次のとおりです。
あなたは多くの詳細を提供していませんが、この問題に対して私が取る一般的なアプローチは次のとおりです。
{id_pair: [1,5], distance: 123}
のようなデータ構造を取得するためのステップを減らすものもありますあなたは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)
擬似コードはひどいですが、それは意図を伝えていると思います。すべてのレコードを他のすべてのレコードと比較しているため、ここでは多くのシャッフルと並べ替えが行われます。私見では、実行するユークリッド距離の合計計算を減らすために、キーペア/距離を中央の場所に保存します(これは危険ですが更新されるブロードキャスト変数など)。