RubyでMnistを表示しつつ、4次元の不思議さを感じる

RubyでMNIST画像を表示する方法はたくさんあると思います。しかし、ここでは敢えてわかりやすさを捨てて、NArrayの行列計算の威力に頼ることにします。配列の形を変えて、並び替えることでMNIST画像をタイルのように並べて表示しようという方針です。画像ファイルの作成はMiniMagickを使うことにしました。Mnistのデータはred-chainerで呼び出しました。

require 'chainer'
require 'mini_magick'

x, t = Chainer::Datasets::Mnist.get_mnist

pixels = Numo::NArray.concatenate(Array.new(100) { |i| x[i][0] })
pixels = pixels.reshape(10, 10, 28, 28).transpose(0, 2, 1, 3)
str = Numo::UInt8.cast(pixels * 255).to_string

img = MiniMagick::Image.import_pixels(str, 280, 280, 8, 'gray', 'png')
img.write('output.png')

output.png

…途中の行列の転置、おわかりいただけたでしょうか。これだけでわかってしまう人はものすごく数学に強い人だと思います(当社比)。

同じ処理をもっとわかりやすく書くと、こんな感じだと思います。

require 'chainer'
require 'mini_magick'

x, t = Chainer::Datasets::Mnist.get_mnist

pixels = Numo::DFloat.zeros(280, 280)

10.times do |i|
  10.times do |j|
    pixels[i * 28...(i + 1) * 28, j * 28...(j + 1) * 28] = x[i * 10 + j][0].reshape(28, 28)
  end
end

str = Numo::UInt8.cast(pixels * 255).to_string

img = MiniMagick::Image.import_pixels(str, 280, 280, 8, 'gray', 'png')
img.write('output.png')

実は、私の場合、reshape や transposeをどうすれば、正しいMNISTの画像が生成できるか、よく考えてもわかりませんでした。
3次元でもわからないのに、4次元になると、本格的に何もわからなくなります。わかる人は尊敬します。

そこで、考えるのはやめて総当りです。permutationを使います。

require 'chainer'
require 'mini_magick'

x, t = Chainer::Datasets::Mnist.get_mnist

pixels = Numo::NArray.concatenate(Array.new(100) { |i| x[i][0] })

[28,28,10,10].permutation.each do |a|
  [0,1,2,3].permutation.each do |b|
    pixels = pixels.reshape(*a).transpose(*b)
    str = Numo::UInt8.cast(pixels * 255).to_string

    img = MiniMagick::Image.import_pixels(str, 280, 280, 8, 'gray', 'png')
    img.write('png/output#{a}#{b}.png')
  end
end

この総当りの過程で生成されたMNistの画像がとても味わい深かったので、いくつかここに載せます。

output[10, 10, 28, 28][0, 1, 3, 2].pngoutput[10, 10, 28, 28][0, 2, 3, 1].pngoutput[10, 10, 28, 28][2, 0, 3, 1].pngoutput[10, 10, 28, 28][2, 3, 0, 1].pngoutput[10, 10, 28, 28][3, 2, 1, 0].pngoutput[10, 28, 10, 28][0, 1, 3, 2].pngoutput[10, 28, 10, 28][1, 3, 0, 2].pngoutput[10, 28, 10, 28][2, 3, 1, 0].pngoutput[10, 28, 10, 28][1, 3, 2, 0].pngoutput[10, 28, 10, 28][3, 2, 1, 0].pngoutput[10, 28, 28, 10][0, 2, 1, 3].pngoutput[10, 28, 28, 10][0, 1, 3, 2].pngoutput[28, 28, 10, 10][2, 0, 1, 3].pngoutput[10, 10, 28, 28][0, 1, 2, 3].pngoutput[10, 28, 28, 10][2, 0, 1, 3].pngoutput[10, 28, 28, 10][3, 1, 2, 0].pngoutput[28, 10, 10, 28][0, 2, 1, 3].pngoutput[10, 28, 10, 28][3, 1, 0, 2].pngoutput[28, 28, 10, 10][0, 3, 2, 1].pngoutput[28, 10, 10, 28][3, 0, 2, 1].pngoutput[10, 10, 28, 28][0, 2, 1, 3].png

私が知ってるMNISTは右下だけです。けれども、驚くべきことに上の模様はすべてMNISTです。ただ4次元に持って行って、すこし配列の並び方を変えると、こんな不思議な画像が生成されてしまいました。はたして、今まで私は本当にMNISTを知っていたのでしょうか。それはMNISTの仮初の姿に過ぎなかったのではないでしょうか。

……

不思議な気分になったところで、本日はこの辺で失礼します。

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.