From 07405d65b99ed365a0a312604f51b96bd07c716d Mon Sep 17 00:00:00 2001 From: Mateusz Sluszniak <56299341+msluszniak@users.noreply.github.com> Date: Tue, 3 Oct 2023 17:11:31 +0200 Subject: [PATCH] Pairwise distances (#177) * Update mix.installs * Add pairwise disctances * Use pairwise distance * Format --- lib/scholar/cluster/affinity_propagation.ex | 8 +- lib/scholar/cluster/k_means.ex | 28 +--- lib/scholar/manifold/tsne.ex | 40 ++--- lib/scholar/metrics/clustering.ex | 20 +-- lib/scholar/metrics/distance.ex | 152 +++++++++++++++++- lib/scholar/neighbors/k_nearest_neighbors.ex | 14 +- .../neighbors/radius_nearest_neighbors.ex | 14 +- test/scholar/manifold/tsne_test.exs | 140 ++++++++-------- 8 files changed, 264 insertions(+), 152 deletions(-) diff --git a/lib/scholar/cluster/affinity_propagation.ex b/lib/scholar/cluster/affinity_propagation.ex index caf7a50b..b2523829 100644 --- a/lib/scholar/cluster/affinity_propagation.ex +++ b/lib/scholar/cluster/affinity_propagation.ex @@ -290,6 +290,10 @@ defmodule Scholar.Cluster.AffinityPropagation do Nx.new_axis(cluster_centers, 0) |> Nx.broadcast(broadcast_shape), axes: [-1] ) + + dist = Scholar.Metrics.Distance.pairwise_euclidean(x, cluster_centers) + + Nx.select(Nx.is_nan(dist), Nx.Constants.infinity(Nx.type(dist)), dist) |> Nx.argmin(axis: 1) end @@ -311,9 +315,7 @@ defmodule Scholar.Cluster.AffinityPropagation do n = Nx.axis_size(data, 0) self_preference = opts[:self_preference] - norm1 = Nx.sum(data ** 2, axes: [1], keep_axes: true) - norm2 = Nx.transpose(norm1) - dist = -1 * (norm1 + norm2 - 2 * Nx.dot(data, [1], data, [1])) + dist = -Scholar.Metrics.Distance.pairwise_squared_euclidean(data) fill_in = cond do diff --git a/lib/scholar/cluster/k_means.ex b/lib/scholar/cluster/k_means.ex index 7a2591d6..20f42827 100644 --- a/lib/scholar/cluster/k_means.ex +++ b/lib/scholar/cluster/k_means.ex @@ -300,24 +300,8 @@ defmodule Scholar.Cluster.KMeans do """ defn predict(%__MODULE__{clusters: clusters} = _model, x) do assert_same_shape!(x[0], clusters[0]) - {num_clusters, _} = Nx.shape(clusters) - {num_samples, num_features} = Nx.shape(x) - - clusters = - clusters - |> Nx.new_axis(1) - |> Nx.broadcast({num_clusters, num_samples, num_features}) - |> Nx.reshape({num_clusters * num_samples, num_features}) - - inertia_for_centroids = - Scholar.Metrics.Distance.squared_euclidean( - Nx.tile(x, [num_clusters, 1]), - clusters, - axes: [1] - ) - |> Nx.reshape({num_clusters, num_samples}) - inertia_for_centroids |> Nx.argmin(axis: 0) + Scholar.Metrics.Distance.pairwise_squared_euclidean(clusters, x) |> Nx.argmin(axis: 0) end @doc """ @@ -343,14 +327,6 @@ defmodule Scholar.Cluster.KMeans do ) """ defn transform(%__MODULE__{clusters: clusters} = _model, x) do - {num_clusters, num_features} = Nx.shape(clusters) - {num_samples, _} = Nx.shape(x) - broadcast_shape = {num_samples, num_clusters, num_features} - - Scholar.Metrics.Distance.euclidean( - Nx.new_axis(x, 1) |> Nx.broadcast(broadcast_shape), - Nx.new_axis(clusters, 0) |> Nx.broadcast(broadcast_shape), - axes: [-1] - ) + Scholar.Metrics.Distance.pairwise_euclidean(x, clusters) end end diff --git a/lib/scholar/manifold/tsne.ex b/lib/scholar/manifold/tsne.ex index d6b27c52..50245689 100644 --- a/lib/scholar/manifold/tsne.ex +++ b/lib/scholar/manifold/tsne.ex @@ -118,9 +118,9 @@ defmodule Scholar.Manifold.TSNE do end defnp fit_n(x, key, opts \\ []) do - {perplexity, learning_rate, num_iters, num_components, exaggeration, init, metric} = + {perplexity, learning_rate, num_iters, num_components, exaggeration, init} = {opts[:perplexity], opts[:learning_rate], opts[:num_iters], opts[:num_components], - opts[:exaggeration], opts[:init], opts[:metric]} + opts[:exaggeration], opts[:init]} x = to_float(x) {n, _dims} = Nx.shape(x) @@ -138,14 +138,14 @@ defmodule Scholar.Manifold.TSNE do x_embedded / Nx.standard_deviation(x_embedded[[.., 0]]) * 1.0e-4 end - p = p_joint(x, perplexity, metric) + p = p_joint(x, perplexity, opts) {y, _} = while {y1, {y2 = y1, learning_rate, p}}, i <- 2..(num_iters - 1), unroll: opts[:learning_loop_unroll] do - q = q_joint(y1, metric) - grad = gradient(p * exaggeration(i, exaggeration), q, y1, metric) + q = q_joint(y1, opts) + grad = gradient(p * exaggeration(i, exaggeration), q, y1, opts) y_next = y1 - learning_rate * grad + momentum(i) * (y1 - y2) {y_next, {y1, learning_rate, p}} end @@ -153,7 +153,7 @@ defmodule Scholar.Manifold.TSNE do y end - defnp pairwise_dist(x, metric) do + defn pairwise_dist(x, opts) do {num_samples, num_features} = Nx.shape(x) broadcast_shape = {num_samples, num_samples, num_features} @@ -167,18 +167,18 @@ defmodule Scholar.Manifold.TSNE do |> Nx.reshape({num_samples, 1, num_features}) |> Nx.broadcast(broadcast_shape) - case metric do + case opts[:metric] do :squared_euclidean -> - Distance.squared_euclidean(t1, t2, axes: [2]) + Distance.pairwise_squared_euclidean(x) :euclidean -> - Distance.euclidean(t1, t2, axes: [2]) + Distance.pairwise_euclidean(x) :manhattan -> Distance.manhattan(t1, t2, axes: [2]) :cosine -> - Distance.cosine(t1, t2, axes: [2]) + Distance.pairwise_cosine(x) :chebyshev -> Distance.chebyshev(t1, t2, axes: [2]) @@ -239,8 +239,12 @@ defmodule Scholar.Manifold.TSNE do low = low, high = high, {max_iters, tol, - perplexity_val = Nx.Constants.infinity(to_float_type(target_perplexity)), - distances, target_perplexity, i = 0} + perplexity_val = + Nx.Constants.infinity( + Nx.Type.to_floating( + Nx.Type.merge(Nx.type(target_perplexity), Nx.type(distances)) + ) + ), distances, target_perplexity, i = 0} }, i < max_iters and Nx.abs(perplexity_val - target_perplexity) > tol do mid = (low + high) / 2 @@ -261,18 +265,18 @@ defmodule Scholar.Manifold.TSNE do (high + low) / 2 end - defnp q_joint(y, metric) do - distances = pairwise_dist(y, metric) + defnp q_joint(y, opts) do + distances = pairwise_dist(y, opts) n = Nx.axis_size(distances, 0) inv_distances = 1 / (1 + distances) inv_distances = inv_distances / Nx.sum(inv_distances) Nx.put_diagonal(inv_distances, Nx.broadcast(0, {n})) end - defnp gradient(p, q, y, metric) do + defnp gradient(p, q, y, opts) do pq_diff = Nx.new_axis(p - q, 2) y_diff = Nx.new_axis(y, 1) - Nx.new_axis(y, 0) - distances = pairwise_dist(y, metric) + distances = pairwise_dist(y, opts) inv_distances = Nx.new_axis(1 / (1 + distances), 2) @@ -297,9 +301,9 @@ defmodule Scholar.Manifold.TSNE do end end - defnp p_joint(x, perplexity, metric) do + defnp p_joint(x, perplexity, opts) do {n, _} = Nx.shape(x) - distances = pairwise_dist(x, metric) + distances = pairwise_dist(x, opts) sigmas = find_sigmas(distances, perplexity) p_cond = p_conditional(distances, sigmas) (p_cond + Nx.transpose(p_cond)) / (2 * n) diff --git a/lib/scholar/metrics/clustering.ex b/lib/scholar/metrics/clustering.ex index 8afeb8d1..7150bd96 100644 --- a/lib/scholar/metrics/clustering.ex +++ b/lib/scholar/metrics/clustering.ex @@ -45,7 +45,7 @@ defmodule Scholar.Metrics.Clustering do iex> Scholar.Metrics.Clustering.silhouette_samples(x, labels, num_clusters: 3) #Nx.Tensor< f32[5] - [0.0, -0.9782054424285889, 0.0, -0.18546819686889648, -0.5929657816886902] + [0.0, -0.9782054424285889, 0.0, -0.18546827137470245, -0.5929659008979797] > """ deftransform silhouette_samples(x, labels, opts \\ []) do @@ -81,7 +81,7 @@ defmodule Scholar.Metrics.Clustering do iex> Scholar.Metrics.Clustering.silhouette_score(x, labels, num_clusters: 3) #Nx.Tensor< f32 - -0.35132789611816406 + -0.35132792592048645 > """ deftransform silhouette_score(x, labels, opts \\ []) do @@ -94,21 +94,9 @@ defmodule Scholar.Metrics.Clustering do defnp inner_and_outer_dist(x, labels, opts) do num_clusters = opts[:num_clusters] - {num_samples, num_features} = Nx.shape(x) + num_samples = Nx.axis_size(x, 0) inf = Nx.Constants.infinity(to_float_type(x)) - broadcast_shape = {num_samples, num_samples, num_features} - - x_a = - x - |> Nx.new_axis(0) - |> Nx.broadcast(broadcast_shape) - - x_b = - x - |> Nx.new_axis(1) - |> Nx.broadcast(broadcast_shape) - - pairwise_dist = Scholar.Metrics.Distance.euclidean(x_a, x_b, axes: [2]) + pairwise_dist = Scholar.Metrics.Distance.pairwise_euclidean(x) membership_mask = Nx.reshape(labels, {num_samples, 1}) == Nx.iota({1, num_clusters}) cluster_size = membership_mask |> Nx.sum(axes: [0]) |> Nx.reshape({1, num_clusters}) dist_in_cluster = Nx.dot(pairwise_dist, membership_mask) diff --git a/lib/scholar/metrics/distance.ex b/lib/scholar/metrics/distance.ex index 27dc3471..d937c36b 100644 --- a/lib/scholar/metrics/distance.ex +++ b/lib/scholar/metrics/distance.ex @@ -448,7 +448,7 @@ defmodule Scholar.Metrics.Distance do merged_type = Nx.Type.merge(Nx.type(x), Nx.type(y)) res = Nx.select(one_zero?, Nx.tensor(0, type: merged_type), res) one_merged_type = Nx.tensor(1, type: merged_type) - one_merged_type - Nx.select(both_zero?, one_merged_type, res) + Nx.max(0, one_merged_type - Nx.select(both_zero?, one_merged_type, res)) end @doc """ @@ -549,4 +549,154 @@ defmodule Scholar.Metrics.Distance do w = Nx.as_type(w, result_type) Nx.weighted_mean(x != y, w, axes: opts[:axes]) |> Nx.as_type(result_type) end + + @doc """ + Pairwise squared euclidean distance. + + ## Examples + + iex> x = Nx.iota({6, 6}) + iex> y = Nx.reverse(x) + iex> Scholar.Metrics.Distance.pairwise_squared_euclidean(x, y) + #Nx.Tensor< + s64[6][6] + [ + [5470, 3526, 2014, 934, 286, 70], + [3526, 2014, 934, 286, 70, 286], + [2014, 934, 286, 70, 286, 934], + [934, 286, 70, 286, 934, 2014], + [286, 70, 286, 934, 2014, 3526], + [70, 286, 934, 2014, 3526, 5470] + ] + > + """ + defn pairwise_squared_euclidean(x, y) do + y_norm = Nx.sum(y * y, axes: [1]) |> Nx.new_axis(0) + x_norm = Nx.sum(x * x, axes: [1], keep_axes: true) + Nx.max(0, x_norm + y_norm - 2 * Nx.dot(x, [-1], y, [-1])) + end + + @doc """ + Pairwise squared euclidean distance. It is equivalent to + Scholar.Metrics.Distance.pairwise_squared_euclidean(x, x) + + ## Examples + + iex> x = Nx.iota({6, 6}) + iex> Scholar.Metrics.Distance.pairwise_squared_euclidean(x) + #Nx.Tensor< + s64[6][6] + [ + [0, 216, 864, 1944, 3456, 5400], + [216, 0, 216, 864, 1944, 3456], + [864, 216, 0, 216, 864, 1944], + [1944, 864, 216, 0, 216, 864], + [3456, 1944, 864, 216, 0, 216], + [5400, 3456, 1944, 864, 216, 0] + ] + > + """ + defn pairwise_squared_euclidean(x) do + x_norm = Nx.sum(x * x, axes: [1], keep_axes: true) + Nx.max(0, x_norm + Nx.transpose(x_norm) - 2 * Nx.dot(x, [-1], x, [-1])) + end + + @doc """ + Pairwise euclidean distance. + + ## Examples + + iex> x = Nx.iota({6, 6}) + iex> y = Nx.reverse(x) + iex> Scholar.Metrics.Distance.pairwise_euclidean(x, y) + #Nx.Tensor< + f32[6][6] + [ + [73.9594497680664, 59.380130767822266, 44.87761306762695, 30.561412811279297, 16.911535263061523, 8.366600036621094], + [59.380130767822266, 44.87761306762695, 30.561412811279297, 16.911535263061523, 8.366600036621094, 16.911535263061523], + [44.87761306762695, 30.561412811279297, 16.911535263061523, 8.366600036621094, 16.911535263061523, 30.561412811279297], + [30.561412811279297, 16.911535263061523, 8.366600036621094, 16.911535263061523, 30.561412811279297, 44.87761306762695], + [16.911535263061523, 8.366600036621094, 16.911535263061523, 30.561412811279297, 44.87761306762695, 59.380130767822266], + [8.366600036621094, 16.911535263061523, 30.561412811279297, 44.87761306762695, 59.380130767822266, 73.9594497680664] + ] + > + """ + defn pairwise_euclidean(x, y) do + Nx.sqrt(pairwise_squared_euclidean(x, y)) + end + + @doc """ + Pairwise euclidean distance. It is equivalent to + Scholar.Metrics.Distance.pairwise_euclidean(x, x) + + ## Examples + + iex> x = Nx.iota({6, 6}) + iex> Scholar.Metrics.Distance.pairwise_euclidean(x) + #Nx.Tensor< + f32[6][6] + [ + [0.0, 14.696938514709473, 29.393877029418945, 44.090816497802734, 58.78775405883789, 73.48469543457031], + [14.696938514709473, 0.0, 14.696938514709473, 29.393877029418945, 44.090816497802734, 58.78775405883789], + [29.393877029418945, 14.696938514709473, 0.0, 14.696938514709473, 29.393877029418945, 44.090816497802734], + [44.090816497802734, 29.393877029418945, 14.696938514709473, 0.0, 14.696938514709473, 29.393877029418945], + [58.78775405883789, 44.090816497802734, 29.393877029418945, 14.696938514709473, 0.0, 14.696938514709473], + [73.48469543457031, 58.78775405883789, 44.090816497802734, 29.393877029418945, 14.696938514709473, 0.0] + ] + > + """ + defn pairwise_euclidean(x) do + Nx.sqrt(pairwise_squared_euclidean(x)) + end + + @doc """ + Pairwise cosine distance. + + ## Examples + + iex> x = Nx.iota({6, 6}) + iex> y = Nx.reverse(x) + iex> Scholar.Metrics.Distance.pairwise_cosine(x, y) + #Nx.Tensor< + f32[6][6] + [ + [0.2050153613090515, 0.21226388216018677, 0.22395789623260498, 0.24592703580856323, 0.30156970024108887, 0.6363636255264282], + [0.03128105401992798, 0.03429150581359863, 0.039331674575805664, 0.049365341663360596, 0.07760530710220337, 0.30156970024108887], + [0.014371514320373535, 0.01644366979598999, 0.020004630088806152, 0.02736520767211914, 0.049365341663360596, 0.24592703580856323], + [0.0091819167137146, 0.010854601860046387, 0.013785064220428467, 0.020004630088806152, 0.039331674575805664, 0.22395789623260498], + [0.006820023059844971, 0.008272230625152588, 0.010854601860046387, 0.01644366979598999, 0.03429150581359863, 0.21226388216018677], + [0.005507469177246094, 0.006820023059844971, 0.0091819167137146, 0.014371514320373535, 0.03128105401992798, 0.2050153613090515] + ] + > + """ + defn pairwise_cosine(x, y) do + x_normalized = Scholar.Preprocessing.normalize(x, axes: [1]) + y_normalized = Scholar.Preprocessing.normalize(y, axes: [1]) + Nx.max(0, 1 - Nx.dot(x_normalized, [-1], y_normalized, [-1])) + end + + @doc """ + Pairwise cosine distance. It is equivalent to + Scholar.Metrics.Distance.pairwise_euclidean(x, x) + + ## Examples + + iex> x = Nx.iota({6, 6}) + iex> Scholar.Metrics.Distance.pairwise_cosine(x) + #Nx.Tensor< + f32[6][6] + [ + [0.0, 0.0793418288230896, 0.1139642596244812, 0.13029760122299194, 0.1397092342376709, 0.14581435918807983], + [0.0793418288230896, 0.0, 0.0032819509506225586, 0.006624102592468262, 0.008954286575317383, 0.01060718297958374], + [0.1139642596244812, 0.0032819509506225586, 1.1920928955078125e-7, 5.82277774810791e-4, 0.0013980269432067871, 0.0020949840545654297], + [0.13029760122299194, 0.006624102592468262, 5.82277774810791e-4, 5.960464477539063e-8, 1.7595291137695312e-4, 4.686713218688965e-4], + [0.1397092342376709, 0.008954286575317383, 0.0013980269432067871, 1.7595291137695312e-4, 0.0, 7.027387619018555e-5], + [0.14581435918807983, 0.01060718297958374, 0.0020949840545654297, 4.686713218688965e-4, 7.027387619018555e-5, 0.0] + ] + > + """ + defn pairwise_cosine(x) do + x_normalized = Scholar.Preprocessing.normalize(x, axes: [1]) + Nx.max(0, 1 - Nx.dot(x_normalized, [-1], x_normalized, [-1])) + end end diff --git a/lib/scholar/neighbors/k_nearest_neighbors.ex b/lib/scholar/neighbors/k_nearest_neighbors.ex index 9ceaab35..0223a3b6 100644 --- a/lib/scholar/neighbors/k_nearest_neighbors.ex +++ b/lib/scholar/neighbors/k_nearest_neighbors.ex @@ -304,25 +304,21 @@ defmodule Scholar.Neighbors.KNearestNeighbors do {num_samples, num_features} = Nx.shape(data) {num_samples_x, _num_features} = Nx.shape(x) broadcast_shape = {num_samples_x, num_samples, num_features} - data = Nx.new_axis(data, 0) |> Nx.broadcast(broadcast_shape) - x = Nx.new_axis(x, 1) |> Nx.broadcast(broadcast_shape) + data_broadcast = Nx.new_axis(data, 0) |> Nx.broadcast(broadcast_shape) + x_broadcast = Nx.new_axis(x, 1) |> Nx.broadcast(broadcast_shape) dist = case metric do {:minkowski, p} -> Scholar.Metrics.Distance.minkowski( - data, - x, + data_broadcast, + x_broadcast, axes: [-1], p: p ) :cosine -> - Scholar.Metrics.Distance.cosine( - data, - x, - axes: [-1] - ) + Scholar.Metrics.Distance.pairwise_cosine(x, data) end {val, ind} = Nx.top_k(-dist, k: default_num_neighbors) diff --git a/lib/scholar/neighbors/radius_nearest_neighbors.ex b/lib/scholar/neighbors/radius_nearest_neighbors.ex index 286670c1..66c378b1 100644 --- a/lib/scholar/neighbors/radius_nearest_neighbors.ex +++ b/lib/scholar/neighbors/radius_nearest_neighbors.ex @@ -281,25 +281,21 @@ defmodule Scholar.Neighbors.RadiusNearestNeighbors do {num_samples, num_features} = Nx.shape(data) {num_samples_x, _num_features} = Nx.shape(x) broadcast_shape = {num_samples_x, num_samples, num_features} - data = Nx.new_axis(data, 0) |> Nx.broadcast(broadcast_shape) - x = Nx.new_axis(x, 1) |> Nx.broadcast(broadcast_shape) + data_broadcast = Nx.new_axis(data, 0) |> Nx.broadcast(broadcast_shape) + x_broadcast = Nx.new_axis(x, 1) |> Nx.broadcast(broadcast_shape) dist = case metric do {:minkowski, p} -> Scholar.Metrics.Distance.minkowski( - data, - x, + data_broadcast, + x_broadcast, axes: [-1], p: p ) :cosine -> - Scholar.Metrics.Distance.cosine( - data, - x, - axes: [-1] - ) + Scholar.Metrics.Distance.pairwise_cosine(x, data) end {dist, dist <= radius} diff --git a/test/scholar/manifold/tsne_test.exs b/test/scholar/manifold/tsne_test.exs index 66ff04e8..884321d4 100644 --- a/test/scholar/manifold/tsne_test.exs +++ b/test/scholar/manifold/tsne_test.exs @@ -101,16 +101,16 @@ defmodule Scholar.Manifold.TSNETest do expected = Nx.tensor([ - [-53.53498458862305, 141.47708129882812], - [62.434783935546875, -8.663802146911621], - [1.1455897092819214, -58.12458038330078], - [15.411615371704102, 31.276975631713867], - [-1.600449562072754, -0.8674168586730957], - [32.58098602294922, -85.54332733154297], - [29.547317504882812, 8.689169883728027], - [22.506656646728516, -9.870643615722656], - [-119.04460906982422, -1.2769389152526855], - [10.55148696899414, -17.096540451049805] + [-40.56040954589844, -68.16744995117188], + [4.963836669921875, 15.062470436096191], + [52.409217834472656, 62.55381393432617], + [3.6575255393981934, -21.331544876098633], + [1.9406723976135254, 9.503897666931152], + [50.68553161621094, 97.37788391113281], + [12.861334800720215, -5.144858360290527], + [-0.08688473701477051, 11.097810745239258], + [-16.302968978881836, 4.448402404785156], + [-69.56835174560547, -105.40238189697266] ]) assert_all_close(embedding, expected) @@ -133,16 +133,16 @@ defmodule Scholar.Manifold.TSNETest do expected = Nx.tensor([ - [-1.8766953945159912, -0.8595688343048096, 2.919325351715088], - [-4.469432830810547, 4.955996036529541, -0.45173633098602295], - [-1.0309523344039917, 9.212959289550781, 2.0173211097717285], - [-7.146204948425293, -3.228529930114746, -2.2784125804901123], - [-7.251376152038574, -3.374234676361084, -2.844855785369873], - [-2.8190832138061523, 3.4020588397979736, 5.268616676330566], - [-1.3570380210876465, 0.8994224667549133, 1.038955807685852], - [12.099328994750977, -4.93052864074707, -2.2077226638793945], - [-0.6444416046142578, -4.907196044921875, -1.334627628326416], - [14.495301246643066, -1.169534683227539, -2.126943826675415] + [-0.720294713973999, 7.20734977722168, -6.391678810119629], + [-7.667532444000244, 3.2291605472564697, 9.317089080810547], + [-0.24787211418151855, -2.148440361022949, -3.8253002166748047], + [2.0674548149108887, 5.781192779541016, 5.811742782592773], + [28.587512969970703, -37.42428207397461, -90.23539733886719], + [-17.36945152282715, 17.298032760620117, 71.87922668457031], + [1.9807157516479492, -1.1625878810882568, 7.809732437133789], + [-1.256453275680542, 3.8095149993896484, 2.0424277782440186], + [-0.840096116065979, 3.5864388942718506, 6.742667198181152], + [-4.535242557525635, -0.17502427101135254, -3.1499686241149902] ]) assert_all_close(embedding, expected) @@ -153,16 +153,16 @@ defmodule Scholar.Manifold.TSNETest do expected = Nx.tensor([ - [10.765605926513672, 15.370049476623535], - [85.68479919433594, -94.71454620361328], - [-135.8154754638672, 90.65518188476562], - [-26.397457122802734, -125.1629638671875], - [-14.444628715515137, 9.014686584472656], - [95.48921203613281, 3.930342197418213], - [-25.713642120361328, -11.954283714294434], - [-11.639439582824707, -12.724642753601074], - [46.66755294799805, -10.349587440490723], - [-24.5968017578125, 135.9352569580078] + [-9.334256172180176, 69.1719741821289], + [12.588174819946289, -16.575504302978516], + [35.39522933959961, 56.1950798034668], + [-31.067523956298828, 178.23777770996094], + [-134.37229919433594, -197.263671875], + [-58.42694854736328, -212.14434814453125], + [26.330211639404297, 16.41795539855957], + [124.88433074951172, -55.47932052612305], + [43.05698776245117, -13.188211441040039], + [-9.053865432739258, 174.62806701660156] ]) assert_all_close(embedding, expected) @@ -173,16 +173,16 @@ defmodule Scholar.Manifold.TSNETest do expected = Nx.tensor([ - [-2.0807719230651855, -7.918057918548584], - [2.831566095352173, -6.691900253295898], - [-12.179752349853516, -12.317314147949219], - [-0.7235263586044312, 10.08445930480957], - [4.194637298583984, 6.559009552001953], - [6.072756767272949, 2.9850857257843018], - [0.30952686071395874, -11.06390380859375], - [1.7182531356811523, 9.67526626586914], - [0.5874165296554565, 3.3545517921447754], - [-0.7300540208816528, 5.332911968231201] + [-1.5070298910140991, 1.7770516872406006], + [1.0464317798614502, 6.572760581970215], + [3.0646090507507324, 0.7151472568511963], + [-3.227736234664917, 0.7268509268760681], + [2.950016498565674, 2.1362667083740234], + [-2.0649163722991943, 1.4648656845092773], + [-0.26644080877304077, 0.9178383946418762], + [0.7323529720306396, -13.481656074523926], + [1.5609462261199951, -0.4036065340042114], + [-2.288417100906372, -0.4257314205169678] ]) assert_all_close(embedding, expected) @@ -193,16 +193,16 @@ defmodule Scholar.Manifold.TSNETest do expected = Nx.tensor([ - [-19.072818756103516, 159.60926818847656], - [1.3988478183746338, 1.6701154708862305], - [-8.892325401306152, -1.8828166723251343], - [-0.24954938888549805, -18.246187210083008], - [17.941997528076172, -106.79717254638672], - [10.342303276062012, 2.533393383026123], - [-4.80264949798584, -0.8543813824653625], - [-134.93646240234375, -27.031400680541992], - [132.14447021484375, -1.7187316417694092], - [6.124286651611328, -7.282605171203613] + [24.384241104125977, 9.98823070526123], + [9.558063507080078, 8.126869201660156], + [-86.89659881591797, -21.715559005737305], + [-1.2773358821868896, -6.753499984741211], + [-5.65122127532959, -13.650869369506836], + [0.5404881238937378, -24.523698806762695], + [2.29805064201355, -14.350067138671875], + [-10.488426208496094, -19.941368103027344], + [123.80097961425781, 99.55735778808594], + [-56.26921463012695, -16.739578247070312] ]) assert_all_close(embedding, expected) @@ -213,16 +213,16 @@ defmodule Scholar.Manifold.TSNETest do expected = Nx.tensor([ - [11.895841598510742, -16.399948120117188], - [23.96310043334961, -20.192819595336914], - [139.05288696289062, -64.61959838867188], - [10.42809009552002, -25.98411750793457], - [14.136453628540039, 4.297189712524414], - [-5.043922424316406, 102.03876495361328], - [-2.542982339859009, -61.87345886230469], - [-177.80848693847656, 11.263525009155273], - [4.459288597106934, -24.146390914916992], - [-18.539642333984375, 95.6163558959961] + [17.54478645324707, -4.611420631408691], + [12.03654670715332, 4.872840881347656], + [6.249638080596924, -1.6746138334274292], + [94.60688018798828, 55.435829162597656], + [10.510167121887207, 4.246967315673828], + [19.59783935546875, 0.7140516638755798], + [-56.172481536865234, -14.603594779968262], + [-3.935652256011963, -2.9040825366973877], + [-116.89958953857422, -42.6511116027832], + [16.4613094329834, 1.176856517791748] ]) assert_all_close(embedding, expected) @@ -253,16 +253,16 @@ defmodule Scholar.Manifold.TSNETest do expected = Nx.tensor([ - [49.713783264160156, -3.4101526737213135], - [-30.54273796081543, 19.592681884765625], - [90.19834899902344, -25.60640525817871], - [29.829374313354492, 6.614677429199219], - [11.306724548339844, 6.371883392333984], - [21.72574806213379, 8.363205909729004], - [55.91529083251953, 0.1634223908185959], - [-81.51355743408203, -23.467527389526367], - [-105.60922241210938, -7.828176498413086], - [-41.02332305908203, 19.206113815307617] + [-67.28031921386719, -6.817644119262695], + [-16.588186264038086, 93.91526794433594], + [14.03242015838623, -4.765425205230713], + [-20.412851333618164, 7.198407173156738], + [6.30552864074707, -16.696596145629883], + [36.38324737548828, -9.629066467285156], + [-26.198514938354492, -6.854467391967773], + [92.41073608398438, -39.33834457397461], + [9.657635688781738, -9.092098236083984], + [-28.308706283569336, -7.919832706451416] ]) assert_all_close(embedding, expected)