BigQuery MLのML.DISTANCE関数で2点のベクトルの距離を簡単に求めてみた

この冬は猫とゼロ距離生活
2023.12.04

無性にザワークラウトが食べたくなったのに近所のスーパーに売ってなくて悶絶しました。


▲ カルディとかなら売ってるのかな

こんにちは。データアナリティクス事業本部 インテグレーション部 機械学習チームのShirotaです。
この記事は、ブログリレー『Google CloudのAI/MLとかなんとか』の4本目の記事になります。

今回はBigQuery ML(以下BQML)にあるML.DISTANCE関数を使って2点間のベクトルの距離を求めてみようと思います。
BQMLについて簡単に説明すると、SQLクエリを利用して機械学習モデルの作成・実行ができるBigQueryのML機能の一つになっています。
様々なモデルの利用や機械学習を実施するにあたって必要な前処理やハイパーパラメータのチューニング・評価などを関数を指定することによって簡単に利用することができ、また普段からデータ分析のためにクエリを書いている人にとっては導入しやすいSQLクエリの文法を用いて実行することができるといった利点があるGoogle CloudのMLサービスとなっています。

ベクトルの距離を求めるということについて

ここで事前に、ベクトルの距離を求めるということはどういうことなのかについて整理しておきます。
ベクトルの距離を求めることによって、対象の類似度を求めることができます。また、それらの情報を用いクラスタリングを行うことができます。
極論を述べるとベクトル化さえできてしまえば距離を求め類似度を調べることができるので、「この言葉とこの言葉は似ているか」といった文字に関することも調べられるため自然言語学習の界隈などでも活用されている考え方となっています。

類似度を考える際に用いる距離について、ここからまとめていきます。

距離の種類

よく用いられる距離の概念についてまとめました。


▲ 以下で説明する距離について図解してみました

  • マンハッタン距離(L1ノルム)

イメージとしては碁盤の目のような道路(日本だと京都や札幌が思い浮かびますが、アメリカだとマンハッタンがこのような道路の都市となっているらしいです)を移動して目的地に行く場合の距離のことをマンハッタン距離と言います。
上図の2次元上にある直角三角形で言うと、斜辺以外の2辺の長さがこれに該当します。
差を2乗するユークリッド距離(後述)と比べて外れ値の影響が小さいことが特徴として挙げられます。

  • ユークリッド距離(L2ノルム)

2点間を直線で結んだ距離のことをユークリッド距離と言います。
図の2次元上にある直角三角形で言うと、斜辺の長さがこれに該当します。
この2点間の距離を求める方法は中学生くらいで習う三平方の定理(ピタゴラスの定理)の公式を適用しているので、どこか見覚えがあると感じた方も多いのではないでしょうか。

  • ミンコフスキー距離(Lpノルム)

上記を一般化した距離をミンコフスキー距離と言います。

  • チェビシェフ距離(L∞ノルム)

p→∞を取ったものはチェビシェフ距離と言い、各座標の差の絶対値が最大になる距離を指しています。
各座標の差の絶対値の最大値を取るので、それ以外のノイズとなる差は考慮されないという特徴があります。
これはチェスの移動と同じ考え方をしているので、別名「チェス盤距離」とも呼ばれています。

2次元のユークリッド距離をBigQueryで求めてみる

ここからは、直感的にも理解しやすい2次元のユークリッド距離を求めていきたいと思います。

ML.DISTANCE関数なしで求めてみる

上記で説明した通り、ユークリッド距離は2点間の最短距離(直線)となっているので関数がなくても計算できそうです。
BigQueryは様々な関数と演算子をサポートしており、ここに用意された関数と演算子を用いればユークリッド距離を求められそうなので試しに計算してみることにしました。

以下のようなテーブルを用意しました。


▲ 他の検証も行っていたため形式的には3次元のベクトルになっています

id1とid2の2点間のユークリッド距離を求めてみることにしました。
(0,4,0)と(3,0,0)のベクトルなので、上記に図解した直角三角形の点Aと点Bの直線距離と実質同じになっています。

ユークリッド距離の計算を実施したクエリは以下になります。計算するためにデータの加工を実行した部分のクエリは今回省略しました。

SELECT
  id_1,
  id_2,
  SQRT(ABS((vector_x_1-vector_x_2)*(vector_x_1-vector_x_2))+ABS((vector_y_1-vector_y_2)*(vector_y_1-vector_y_2))+ABS((vector_z_1-vector_z_2)*(vector_z_1-vector_z_2))) as distance
FROM test4

上記クエリの実行結果は以下のようになりました。


▲ 求めたかった距離は出せたけど

さて、上記は3次元(実質は2次元)のベクトルのユークリッド距離を求める計算をクエリで実行したものでした。
何が言いたいのかお分かりの方も多いのではないでしょうか。そう、 長いし複雑 です。
3次元でさえこの長さなので、高次元のユークリッド距離を求めようとした場合には地獄絵図が目に浮かびます。やれないことはないですがほぼ確実にやりたくはないです。

ML.DISTANCE関数を用いて求めてみる

では、BQMLに用意されているML.DISTANCE関数を用いてユークリッド距離を求めてみましょう。
ML.DISTANCE関数のリファレンスは以下公式ドキュメントを参考にしました。

上記リファレンスに記載があるのですが、ここで用いるベクトルは ARRAY型 で持たせる必要があります。
今回は ARRAY でスキーマを作成し、以下クエリでレコードを挿入しました。

INSERT `XXXXXXXXXXX.test.ml-distance-test`
  VALUES ([0,4,0],[3,0,0])

テーブルは以下のようになっています。


▲ ML.DISTANCE関数を使う都合上この形にしました

あとは、以下のクエリを実行します。
ML.DISTANCE関数は引数でユークリッド距離・マンハッタン距離・コサイン類似度(こちらの記事 で以前簡単にですが説明しているのでよかったら参考にしてください)を選べるので、今回はマンハッタン距離もついでに計算してみました。

SELECT
  vector1,
  vector2,
  ML.DISTANCE(vector1, vector2, 'EUCLIDEAN') AS euclidean_distance,
  ML.DISTANCE(vector1, vector2, 'MANHATTAN') AS manhattan_distance
   FROM `XXXXXXXXXXX.test.ml-distance-test`

上記クエリの実行結果は以下のようになりました。


▲ サクッとついでにマンハッタン距離も出せた

データをARRAY型で持たせる必要があったりとML.DISTANCE関数の利用には若干の制約がありますが、高次元になろうとも数式を長々と書くことなくこの関数1つでユークリッド距離が求められるのはとても便利です。

機械学習に便利な関数が揃っているBQML

今回はBQMLの関数を用いてユークリッド距離を求めてみました。

さて、BQMLには様々な機械学習に便利な関数が揃っていると前述しました。例えば、データの前処理でNULL値を置き換える時に利用できるML.IMPUTER関数があったり時系列予測のモデルを呼び出すML.FORECAST関数があったりと前処理から学習・推論・評価までをBQMLの関数で実施できます。
実際に関数を使ってみて、クエリの記述に抵抗がなければ機械学習を簡単に始めることができるのではないかと思いました。
今回比較対象として計算をBigQueryのクエリ上で書いたものも実行してみましたが、勿論簡単な計算ならこのように実行することも選択肢に入ってくると思います。
BQMLの関数を使う恩恵が大きくなりそうなユースケースを見極め、利用していくことが大事なのではないでしょうか。

このブログが、BQMLの関数を触ってみたい人の助けになれば幸いです。