RubyでUMAPをできるgemを作った話RubyでUMAPをできるgemを作った話
RubyでUMAPをできるgemを作った話

介绍

统一流形逼近和投影 (UMAP) 是一种通过降维的可视化方法,通常与 t-SNE 一起使用。

用 Ruby 语言执行机器学习时,瘤胃我认为有很多情况下你使用 gem 调用。瘤胃有t-SNE但不是UMAP。

这一次,它是一个 C++ 库乌马普红宝石绑定我创造了它,所以我会在我忘记之前记录它。

让我们在没有 Ruby 库的情况下创建一个绑定

Ruby 语言在数据分析领域是一种相对次要的语言,因此通常没有库可以实现您想做的事情。在这种情况下,有一种方法可以找到 C 语言或 Rust 语言等库并创建 Ruby 绑定。 GitHub 搜索允许您按语言搜索代码。使用它来查找 C 语言库。 GitHub 允许你标记你的项目,所以检查目标标记也是有效的。但是,UMAP 似乎很难实现,我找不到实现UMAP 的C 语言库。相反,我找到了一个在 C++ 中实现 UMAP 的库。那是乌马普是。

Umappp - UMAP 的 C++ 实现

乌马普是由 Aaron Lun 实现的 C++ 库。 R的uwot它似乎是参考开发的虽然尚未测量,但可以预期高性能,因为它是用 C++ 实现并使用 OpenMP。

从 Ruby 语言调用 C++ 函数

使用 C++ 的扩展库在 R 语言中很常见,但使用 C++ 的扩展库在 Ruby 语言中的使用并不广泛。最近,Rust 开始流行起来,我偶尔会看到有人用 Rust 做 Ruby 扩展,但我没有看到很多新的 C++ 扩展库。

但是在 Ruby 中创建 C++ 绑定比您想象的要容易。我没有使用 C++ 的经验,但我能够从 Ruby 调用 C++。

在 C++ 中编写 Ruby 扩展有两种方法。一,另一个是扩展程序是。这次Numo::NArray和 C++numo.hpp我用大米是因为我想用。

我将 Umappp 和 Umappp 依赖的所有 C++ 文件放在 Vendor 目录中,并将其作为 Gem 分发,以便我可以在运行时编译 C++。我不知道怎么写extconf.rb,所以我根据其他项目进行了调整。

Rice - 用于 C++ 扩展的 Ruby 接口

是一个用 Ruby 编写 C++ 扩展的库,由 Paul Brannan、Charlie Savage 和 Jason Roelofs 开发。通过使用 Rice,您可以从 C++ 代码中定义 Ruby 模块和方法,如下所示。

#include <rice/rice.hpp>
#include <rice/stl.hpp>

using namespace Rice;

Hash umappp_default_parameters(Object self)
{
  Hash d;
//<中略>
  return d;
}

//<中略>

extern "C" void Init_umappp()
{
  Module rb_mUmappp =
      define_module("Umappp")
          .define_singleton_method("umappp_run", &umappp_run)
          .define_singleton_method("umappp_default_parameters", &umappp_default_parameters);
}

在 Ruby 中运行 UMAP

针对著名的 iris 数据集和 MNIST 数据库运行 UMAP。

鸢尾花数据集

该数据集测量三种鸢尾花的萼片长度和宽度以及花瓣长度和宽度。

RubyでUMAPをできるgemを作った話
图片来源:https://machinelearninghd.com/iris-dataset-uci-machine-learning-repository-project/

红宝石代码:

require "umappp"
require "datasets-numo-narray"
require "gr/plot"

iris = Datasets::LIBSVM.new("iris").to_narray
d = iris[true, 1..-1]
l = iris[true, 0]

r = Umappp.run(d)
x = r[true, 0]
y = r[true, 1]
s = [2000] * l.size

GR.scatter(
  x, y, s, l,
  title: "iris",
  colormap: 16,
  colorbar: true
)
gets

RubyでUMAPをできるgemを作った話

簇被清楚地分开。这里,标签 0 (setosa) 是浅蓝色,标签 1 (versicolor) 是淡紫色,标签 2 (virginica) 是洋红色。 setosa 组与其他组明显分开,但 versicolor 和 virginica 部分重叠。

(旁白)表格数据的GUI显示

在 Ruby 中,很难在 CUI 中查看大型表数据。在这样的时候红琥珀一个名为的数据框和一个用于其GUI显示的自制工具红琥珀观也可用于在 GUI 中显示表格数据。

# irisデータセットをテーブルとして表示する
iris = Datasets::LIBSVM.new("iris")
dataframe = RedAmber::DataFrame.new(iris).view
# 結果をテーブルとして表示する
RedAmber::DataFrame.new(x: x.to_a, y: y.to_a, l: l.to_a).view

*这是一个正在进行的工作,所以这只是一个你可以做这样的事情的故事。

RubyでUMAPをできるgemを作った話

MNIST 数据库

接下来,让我们降低 MNIST 的维度,这是一个手写数字的图像数据集。

RubyでUMAPをできるgemを作った話

红宝石代码:

require "umappp"
require "datasets"
require "gr/plot"
require "etc"

mnist = Datasets::MNIST.new

pixels = []
labels = []
mnist.each_with_index do |r, _i|
  pixels << r.pixels
  labels << r.label
end

puts "start umap"
nproc = Etc.nprocessors
n = nproc > 4 ? nproc - 1 : nproc
d = Umappp.run(pixels, num_threads: n, a: 1.8956, b: 0.8006)
puts "end umap"

x = d[true, 0]
y = d[true, 1]
s = [500] * x.size

GR.scatter(x, y, s, labels, colormap: 0)

gets

在安装了OpenMP的环境下,如果你用htop命令监控CPU使用率,你会发现中间有很多核被用于计算。簇被非常整齐地分开。

RubyでUMAPをできるgemを作った話

如果我们比较 GR.rb 颜色图中的标签,我们会得到:这是,官方 UMAP 结果几乎一样,可以看到UMAP可以正确执行。 5-3-8 和 4-9-7 似乎很相似。手写字符 4 和 9 之间的相似之处是直观的,实际上是最接近的。我有把 1 写成 7 的习惯,但似乎没有多少人这样做。

RubyでUMAPをできるgemを作った話

我将尝试 3D 显示。在这里,指定ndim: 3。 GR.rb有GIF动画输出功能,用起来吧。

require "umappp"
require "datasets" # red-datasets https://github.com/red-data-tools/red-datasets
require "gr/plot"  # GR.rb https://github.com/red-data-tools/GR.rb
require "etc"

mnist = Datasets::MNIST.new

pixels = []
labels = []
mnist.each_with_index do |r, _i|
  pixels << r.pixels
  labels << r.label
end

puts "start umap"
nproc = Etc.nprocessors
n = nproc > 4 ? nproc - 1 : nproc
d = Umappp.run(pixels, ndim: 3, num_threads: n, a: 1.8956, b: 0.8006)
puts "end umap"

x = d[true, 0]
y = d[true, 1]
z = d[true, 2]

GR.beginprint("mnist.gif")
30.times do |i|
  GR.scatter3(x, y, z, labels, title: "mnist", colormap: 0, backgroundcolor: 1, rotation: i * 3)
end
GR.endprint

RubyでUMAPをできるgemを作った話

不幸的是,GR.rb 只是按顺序调用polymarker3d,所以一些应该在后面的点在前面,而一些应该在前面的点在后面。但我认为气氛是传播的。有趣的是,保持了与 2D 的位置关系的一致性。

作为一个反思点,在使用 GR.rb 的可视化中,无法知道哪组颜色对应于哪个数字(无法显示颜色条之类的东西),所以我认为有必要改进这一点。。

写这篇文章的人数学不好,根本不了解UMAP的具体机制。所以我不能说UMAP有什么好处,但我觉得用UMAP检查你关心的数据如果它工作得这么快,会很方便。我的印象是结果出来的速度比 t-SNE 快得多。此外,我觉得 UMAP 结果的可重复性比我预期的要高。

综上所述

通过从 Ruby 调用 C++,可以从 Ruby 使用 UMAP。据我所知,这是第一个可以在 Ruby 中执行 UMAP 的库,包括绑定。这样一来,可以用 Ruby 完成的另一件事增加了。

[公关]
对于那些想要在 Ruby 中创建数据分析工具的人红色数据工具有一个社区叫基本上我是自己开发的,但是如果你在实现上有什么问题,或者你有想法想实现但是不知道怎么做,我们或许可以和你协商,所以请drop经过。

这就是本文的内容。


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308629283.html

相关文章: