Vagrant + rbenv + pyenv で機械学習の勉強用環境構築

 機械学習の勉強をしようと下記書籍を読み始めました。機械学習といえばやはり言語はPythonなのですが、普段Rubyをメインで使っている自分としては、Rubyで同様のことができないかなと思い、書籍のサンプルコード実行の為の環境に加えてRubyの実行環境も用意し、Pythonのサンプルコードの内容をRubyに置き換えていくことに挑戦してみたいと思います。結果として機械学習はやっぱりPythonだね、ということになる可能性は大いにありますが。。

ITエンジニアのための機械学習理論入門
https://www.amazon.co.jp/IT-ebook/dp/B016Q22IX2/

 ということでまず今回は環境作成です。環境はVagrantで作っておいた方があとで何かと便利なので、VagrantでCentOSのVMを用意し、そこにrbenvとpyenvをインストールしてRubyとPythonの実行環境を用意します。CentOSを使うのは、上記書籍での例でCentOSが使われているためです。

Vagrantfile

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  # Every Vagrant virtual environment requires a box to build off of.
  config.vm.box = "esss/centos-7.1-desktop"

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  config.vm.network :private_network, ip: "192.168.59.104"

  config.vm.provider "virtualbox" do |vb|
    vb.customize ["modifyvm", :id, "--memory", "2048"]
  end

  config.vm.provision :shell, path: "provisionings/libs.sh"
  config.vm.provision :shell, path: "provisionings/rbenv.sh"
  config.vm.provision :shell, path: "provisionings/pyenv.sh"
  config.vm.provision :shell, path: "provisionings/ruby2.3.1.sh"
  config.vm.provision :shell, path: "provisionings/anaconda2-4.1.1.sh"
  config.vm.provision :shell, path: "provisionings/env.sh"
  config.vm.provision :shell, path: "provisionings/os_env.sh"
end

 サンプルコードの実行結果としてグラフを表示するためにGUI環境が必要なので、Vagrantのboxには esss/centos-7.1-desktop を使用します。

Vagrant box esss/centos-7.1-desktop | Atlas by HashiCorp

 必要なライブラリやrbenv, pyenv等のインストール用の設定は provisionings ディレクトリを作成してその下にまとめ、 プロビジョニングで実行されるようにします。ファイルを細分化するのは、あとで一部だけ変更して再実行したいときに、そのファイルだけ run オプションで "always" を指定すれば、 vagrant reload 時に再実行できるようにするためです。例えば provisionings/env.sh の内容を変更して再実行したいときは、下記のような記述に変更して vagrant reload します。

config.vm.provision :shell, path: "provisionings/env.sh", run: "always"

 Rubyの違うバージョンを追加でインストールしたいときは、provisionings/ruby2.0.0.sh のようにファイルを用意して、下記のような記述を追加して vagrant reload すればインストールされます。

config.vm.provision :shell, path: "provisionings/ruby2.0.0.sh", run: "always"

 一度実行した後は run: "always" を削除しておけば、次回以降は実行されません。また、各プロビジョニングファイルの内容はデフォルトで root アカウントとして実行されるので、 sudo は不要です。(参考:下記ドキュメントページの privileged についての説明を参照)

www.vagrantup.com

各プロビジョニングファイル

provisionings/libs.sh

#!/bin/bash

yum -y update
yum install -y git
yum install -y openssl-devel readline-devel zlib-devel

 rbenvとpyenvのインストールに必要なgit等をインストールしておきます。

provisionings/rbenv.sh

#!/bin/bash

RBENV_ROOT=/usr/local/rbenv

git clone https://github.com/sstephenson/rbenv.git ${RBENV_ROOT}
git clone https://github.com/sstephenson/ruby-build.git ${RBENV_ROOT}/plugins/ruby-build

echo "export RBENV_ROOT=${RBENV_ROOT}" >> /etc/profile.d/rbenv.sh
echo 'export PATH="${RBENV_ROOT}/bin:$PATH"' >> /etc/profile.d/rbenv.sh
echo 'eval "$(rbenv init -)"' >> /etc/profile.d/rbenv.sh
source /etc/profile.d/rbenv.sh
rbenv --version

${RBENV_ROOT}/plugins/ruby-build/install.sh

 システムワイドで rbenv が使えるように /usr/local/rbenv にインストールします。また、ログイン時にパスが通るように /etc/profile.d/rbenv.sh として設定ファイルを追加します。ちなみに rbenv の環境構築についてググっていくつか記事を見てみましたが、 ruby-build の install.sh の実行について書かれていない記事が多かった気がしました。私の環境ではこれを実行しないと ruby-build が使えるようにならなかったのですが、環境によるものなんですかね?

provisionings/pyenv.sh

#!/bin/bash

PYENV_ROOT=/usr/local/pyenv

git clone https://github.com/yyuu/pyenv.git ${PYENV_ROOT}

echo "export PYENV_ROOT=${PYENV_ROOT}" >> /etc/profile.d/pyenv.sh
echo 'export PATH="${PYENV_ROOT}/bin:$PATH"' >> /etc/profile.d/pyenv.sh
echo 'eval "$(pyenv init -)"' >> /etc/profile.d/pyenv.sh
source /etc/profile.d/pyenv.sh

 pyenv も rbenv と同様にインストールします。pyenv では rbenv の ruby-build にあたるものは不要のようです。

provisionings/ruby2.3.1.sh

#!/bin/bash

rbenv install 2.3.1

 ruby2.3.1をインストールします。古い rbenv だと ruby をインストールした後に rehash が必要でしたが、最近のバージョンでは rehash は不要になっています。

github.com

provisionings/anaconda2-4.1.1.sh

#!/bin/bash

pyenv install anaconda2-4.1.1
pyenv rehash

 Pythonの実行環境を anaconda を使って用意します。書籍の実行環境のセットアップの説明では、必要なツールやライブラリが一括でセットアップされるように Enthought Canopy を使用する方法が紹介されていますが、 anaconda によるインストールでも機械学習関連のライブラリが一通り一括でインストールされるため、こちらを使用しています。また、書籍のサンプルではpython2.7系を使っているので、anacondaの2系を使用します。書籍のサンプル実行において必要なライブラリは下記の通りです。

  • NumPy
  • SciPy
  • matplotlib
  • pandas
  • PIL
  • scikit-learn
  • IPython

provisionings/env.sh

#!/bin/bash

rbenv global 2.3.1
pyenv global anaconda2-4.1.1

 rbenvとpyenvで使用するrubyとpythonのバージョンを指定します。特にディレクトリでの切り分けも現状は必要ないので、globalで同じバージョンを使用します。

provisionings/os_env.sh

#!/bin/bash

echo 'vagrant' | passwd --stdin vagrant

 Vagrantで作成したVMのユーザとパスワードはいずれもvagrantだと思っていたのですが、GUIでそのパスワードだとログインできなかったため、ここで vagrant ユーザのパスワードを設定し直しています。

起動後の確認

 vagrant up 後の ruby と python のバージョンを確認してみます。

$ vagrant ssh
Last login: Sun Nov  6 06:25:32 2016
[vagrant@localhost ~]$ 
[vagrant@localhost ~]$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
[vagrant@localhost ~]$ 
[vagrant@localhost ~]$ python -V
Python 2.7.12 :: Anaconda 4.1.1 (64-bit)

 想定通りのバージョンが使われるようになっています。また、GUI環境でログインし、IPythonからサンプルスクリプトを実行したところ、正しく実行されグラフが表示されました。

f:id:akanuma-hiroaki:20161106153533p:plain

vagrantディレクトリのマウントエラーの対処

 ここまでの手順を終えた後に vagrant reload もしくは、 vagrant halt / vagrant up した場合に、下記のようなエラーが出て vagrant ディレクトリのマウントに失敗することがあります。

Failed to mount folders in Linux guest. This is usually because
the "vboxsf" file system is not available. Please verify that
the guest additions are properly installed in the guest and
can work properly. The command attempted was:

mount -t vboxsf -o uid=`id -u vagrant`,gid=`getent group vagrant | cut -d: -f3` vagrant /vagrant
mount -t vboxsf -o uid=`id -u vagrant`,gid=`id -g vagrant` vagrant /vagrant

The error output from the last command was:

/sbin/mount.vboxsf: mounting failed with the error: No such device

 ホストOSとゲストOS間のディレクトリ共有機能は Vagrant の Guest Additions によって提供されていますが、 yum -y update することで kernel が更新された場合に、古いバージョンの Kernel でビルドされた Guest Additions が動作しなくなるためにエラーになるようです。VMは起動していますので、 vagrant ssh でログインした後に下記コマンドを実行して Guest Additions を再インストールしてから vagrant reload することで解消されます。

$ sudo /etc/rc.d/init.d/vboxadd setup
Removing existing VirtualBox non-DKMS kernel modules       [  OK  ]
Building the VirtualBox Guest Additions kernel modules
Building the main Guest Additions module                   [  OK  ]
Building the shared folder support module                  [  OK  ]
Building the OpenGL support module                         [  OK  ]
Doing non-kernel setup of the Guest Additions              [  OK  ]
Starting the VirtualBox Guest Additions                    [  OK  ]

 Guest Additions の再インストールを自動化してくれる、 vagrant-vbguest というプラグインがあるようなのですが、私の環境ではこのプラグインをインストールしようとするとエラーになってしまいました。原因を追求して解決したいところではありますが、本来の目的とは違うので、ひとまず手動での対応ができれば良いということにしておきます。

 ちなみに書き終わってから知ったのですが、 anyenv という、rbenv や pyenv など複数の **env をまとめて管理できるものがあるようなので、いずれはそちらに乗り換えてみたいと思います。