洋食の日記

「だ・である」調ではなく「です・ます」調で書きはじめれば良かったなと後悔してる人のブログです

RumaleにHessian Eigenmapsによる次元削減を追加した

はじめに

最近、Rumaleのリリース記事を書くことをすっかり忘れていた。最近リリースしたv0.28.1では、多様体学習の非線形次元削減手法であるHessian Eigenmaps(a.k.a. Hessian Locally Linear Embedding, HLLE)を実装した。HLLEは、各点の十分に小さい近傍では、多様体上の近くの点までの測地線距離が、対応するパラメータ点間のユークリッド距離と同一となるという、Local isometryをもとにデータ分布の構造を捉える。

Donoho, D. L., and Grimes, C., "Hessian eigenmaps: Locally linear embedding techniques for high-dimensional data," In Proc. Natl. Acad. Sci. USA, vol. 100, no. 10, pp. 5591-5596, 2003.

インストール

HLLEは、Rumale::Manifoldに含まれる。HLLEだけが必要な場合は、以下でインストールできる。

gem install rumale-manifold

HLLEのアルゴリズム固有値分解を必要とするので、Numo::TinyLinalgもインストールする。

gem install numo-tiny_linalg

簡単な実験

多様体学習の例題としてよく用いられる、長方形がくるりと巻かれた三次元のスイスロールデータを、二次元空間に展開することを試す。データ点のプロットに、Numo::Gnuplotを使いたいので、これをインストールする。

brew install gnuplot
gem install numo-gnuplot

多様体学習の代表的な手法であるLocally Linear Embedding(LLE)とHLLEによりスイスロールデータを二次元空間に展開するスクリプトは以下のようになる。

# frozen_string_literal: true

require 'numo/gnuplot'
require 'numo/tiny_linalg'
Numo::Linalg = Numo::TinyLinalg unless defined?(Numo::Linalg)

require 'rumale/manifold/locally_linear_embedding'
require 'rumale/manifold/hessian_eigenmaps'
require 'rumale/utils'

def make_swiss_roll(n_samples: 1000, random_seed: nil)
  rng = Random.new(random_seed || srand)
  theta = 1.5 * Math::PI * (1 + 2 * Rumale::Utils.rand_uniform(n_samples, rng))
  y = 20 * Rumale::Utils.rand_uniform(n_samples, rng)
  x = theta * Numo::NMath.cos(theta)
  z = theta * Numo::NMath.sin(theta)
  data = Numo::NArray.vstack([x, y, z]).transpose.dup
  [data, theta]
end

x, y = make_swiss_roll(n_samples: 2000, random_seed: 20231224)
y = Numo::Int32.cast(y)

plots = y.to_a.uniq.sort.map { |l| [x[y.eq(l), 0], x[y.eq(l), 1], x[y.eq(l), 2], { t: l.to_s }] }

Numo.gnuplot do
  set(terminal: 'png')
  set(output: 'swissroll3d.png')
  set(ticslevel: 0)
  set(view: [84, 11, 1, 1])
  splot(*plots)
end

# trn = Rumale::Manifold::LocallyLinearEmbedding.new(n_components: 2, n_neighbors: 10)
trn = Rumale::Manifold::HessianEigenmaps.new(n_components: 2, n_neighbors: 10)
z = trn.fit_transform(x)

plots = y.to_a.uniq.sort.map { |l| [z[y.eq(l), 0], z[y.eq(l), 1], { t: l.to_s }] }

Numo.gnuplot do
  set(terminal: 'png')
  set(output: 'swissroll2d.png')
  set(ticslevel: 0)
  plot(*plots)
end

これを実行した結果が次のようになる。まずは元データ、

3次元のスイスロールデータ

次にLLEにより二次元空間に展開したデータ、

LLEにより2次元に埋め込まれたスイスロールデータ

そしてHLLEにより二次元空間に展開したデータ、

HLLEにより2次元に埋め込まれたスイスロールデータ

LLEでは、もとの長方形が歪んだ形で二次元空間に展開されているが、HLLEは歪むことなく展開されている。スイスロールの他のデータでも、HLLEはLLEよりも、非線形なデータ分布を捉えることができる。

おわりに

今回、多様体学習の手法の中でHessian Eigenmaps(HLLE)を選択したのは、以下のNeurIPSのproceedingsで使われていたためである。

Horan, D., Richardson, E., and Weiss, Y., "When Is Unsupervised Disentanglement Possible?," NeurIPS'21, Vol. 34, pp. 5150--5161, 2021.

タイトルの通りで、教師なしのdisentaglement(和訳がわからないが高次元空間で入り組んでるデータから本質的な要素を取り出そうという感じ)問題に挑戦したものである。HLLEで非線形次元削減して、FastICAで独立な成分を求めることで、平均相関係数を評価尺度に用いた実験では、Auto-endcoderなどよりも良い結果を得ている。今では古典的な手法となったHLLEとFastICAで、distangledな表現が得られるというのがおもしろい。

古典的機械学習アルゴリズムは、いわゆるプロ驚き屋が話題にしないので影が薄い感じだが、毎年なにかしら有名な国際会議で研究発表がされている。そんなわけで、2024年もRumaleの開発は続いていく。

github.com