Rails × ruby-spacy 環境を Docker で構築して自然言語処理に入門する
Rails で構築しているアプリケーションで自然言語処理を行いたかったので、Ruby で自然言語処理を行えるライブラリの ruby-spacy の検証を行うために docker で環境構築を行うことにしました。
後述しますが、単なる gem ではなくある程度準備が必要なもので、はまった個所もいくつかあったので、備忘録として残します。
ruby-spacy とは
ruby-spacy とは Yoichiro Hasebe さんによって開発されたライブラリで、Python 用の自然言語処理ライブラリである spaCy を Ruby で利用できるようにしたライブラリです。
spaCy とは、Python/Cython で構築された自然言語処理を行うためのライブラリで、訓練済みの統計モデルを使用することができます。
参考: https://spacy.io/
参考: https://ja.wikipedia.org/wiki/SpaCy
また、日本語のテキストを解析するための言語モデルであり、リクルートと国立国語研究所の共同研究によって開発された GiNZA を spaCy でも使用することができます。
参考: https://www.recruit.co.jp/newsroom/2019/0402_18331.html
上記のように、ruby-spacy では Python ライブラリである spaCy を呼び出しています。
内部で PyCall という Ruby から Python を呼び出すための gem を利用しているので、Rails 環境の中に Python 環境を同居させる必要があります。
サンプルリポジトリ
ディレクトリ構造
こんな感じでプロジェクトディレクトリに空のファイル群を作成しておきます。
.
├─ compose.yml
└─ rails
├─ Dockerfile
├─ Gemfile
├─ Gemfile.lock
└─ entrypoint.sh
データベースに postgres
を使用していますが、今回は使用しないので任意で大丈夫です。
Gemfile.lock
は空のままで大丈夫です。
entrypoint.sh
には、サーバーのプロセスが残っている場合、新たにサーバーを起動できないため、それを防ぐために起動時にプロセスをいったん削除するコマンドが記述されています。
compose.yml
version: '3'
services:
db:
container_name: postgres-spacy-sample
image: postgres:16.0
ports:
- "5432:5432"
volumes:
- pg-data:/var/lib/postgresql/data
environment:
- POSTGRES_DATABASE=postgres
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- POSTGRES_ROOT_PASSWORD=root
api:
container_name: api-spacy-sample
build:
context: ./rails
command: /bin/bash
volumes:
- ./rails:/usr/src/app
ports:
- 3000:3000
depends_on:
- db
tty: true
stdin_open: true
volumes:
pg-data:
bin:
driver: local
Dockerfile
FROM ruby:3.3.0
RUN apt-get update -qq && apt-get install -y vim
RUN apt-get install -y build-essential
RUN apt-get install -y curl wget zip git
RUN git clone https://github.com/pyenv/pyenv.git ~/.pyenv
RUN [ \
"/bin/bash", "-c", \
" \
source ~/.bashrc \
&& echo 'export PYENV_ROOT=\"$HOME/.pyenv\"' >> ~/.bashrc \
&& echo 'command -v pyenv >/dev/null || export PATH=\"$PYENV_ROOT/bin:$PATH\"' >> ~/.bashrc \
&& echo 'eval \"$(pyenv init -)\"' >> ~/.bashrc \
&& source ~/.bashrc \
&& export CONFIGURE_OPTS=\"--enable-shared\" \
&& pyenv install 3.10.6 \
&& pyenv global 3.10.6 \
&& pip install spacy==3.6.0 \
&& pip install ginza==5.1.3 \
&& python -m spacy download ja_core_news_sm \
"]
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
COPY Gemfile /usr/src/app/Gemfile
COPY Gemfile.lock /usr/src/app/Gemfile.lock
RUN gem update --system
RUN bundle update --bundler
RUN bundle install
COPY . /usr/src/app
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
Gemfile
source "https://rubygems.org"
git_source(:github) {|repo| "https://github.com/#{repo}.git" }
ruby "3.3.0"
gem "rails", "~> 7.1.2"
entrypoint.sh
#!/bin/bash
set -e
rm -f /usr/src/app/tmp/pids/server.pid
exec "$@"
Rails アプリケーションの作成
下記コマンドを実行して、Rails
アプリケーションを作成します。
docker compose run --rm api rails new . --skip --database=postgresql --api --skip-bundle
Gemfile, database.yml を書き換える
以下のように Gemfile
を書き換えます。
また、config/database.yml
も作成した環境に合わせて書き換えます。
Gemfile
source "https://rubygems.org"
git_source(:github) {|repo| "https://github.com/#{repo}.git" }
ruby "3.3.0"
gem "bootsnap", require: false
gem "pg", "~> 1.1"
gem "puma", "~> 6.4.0"
gem "rails", "~> 7.1.2"
gem "ruby-spacy", "~> 0.2.2"
gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby]
config/database.yml
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: postgres
password: password
host: db
port: 5432
development:
<<: *default
database: postgres_dev
password: password
test:
<<: *default
database: postgres_test
password: password
gem をインストールする
下記コマンドを実行して、gem をインストールします。
docker compose run --rm api bundle install
Docker コンテナを立ち上げる
コンテナをビルドして立ち上げます。
docker compose build --no-cache
docker compose up -d
Postgresql の DB を作成する
現状だと DB が存在しないため作成する必要があります。
rails
コンテナ環境に入ります。
docker compose exec api /bin/bash
rails
コンテナ内で db:create
して DB を作成します。
rails db:create
spaCy の導入について
先述したように ruby-spacy は、Python 用ライブラリの spaCy のラッパーなので利用するためには、Python の環境を構築する必要があります。
Dockerfile で該当する部分を以下に記します。
# Dockerfile
# ( 中略 )
RUN apt-get install -y build-essential
RUN apt-get install -y curl wget zip git
RUN git clone https://github.com/pyenv/pyenv.git ~/.pyenv
RUN [ \
"/bin/bash", "-c", \
" \
source ~/.bashrc \
&& echo 'export PYENV_ROOT=\"$HOME/.pyenv\"' >> ~/.bashrc \
&& echo 'command -v pyenv >/dev/null || export PATH=\"$PYENV_ROOT/bin:$PATH\"' >> ~/.bashrc \
&& echo 'eval \"$(pyenv init -)\"' >> ~/.bashrc \
&& source ~/.bashrc \
&& export CONFIGURE_OPTS=\"--enable-shared\" \
&& pyenv install 3.10.6 \
&& pyenv global 3.10.6 \
&& pip install spacy==3.6.0 \
&& pip install ginza==5.1.3 \
&& python -m spacy download ja_core_news_sm \
"]
# ( 中略 )
bash を使う
後述する理由により、直接 Python をインストールせずに、Pyenv を用いて Python の仮想環境を構築します。
dockerfile で RUN コマンドを書くと /bin/sh が使われますが、sh だと、pyenv をインストールすることができなかったので、bash を使うようにしています。
RUN [ \
"/bin/bash", "-c", \
( 中略 )
pyenv のインストール
直接 Python をインストールしてそこにライブラリを pip install すると、PEP 668 エラーが発生します。
RUN apt-get install -y curl wget zip git
RUN git clone https://github.com/pyenv/pyenv.git ~/.pyenv
RUN [ \
"/bin/bash", "-c", \
" \
source ~/.bashrc \
&& echo 'export PYENV_ROOT=\"$HOME/.pyenv\"' >> ~/.bashrc \
&& echo 'command -v pyenv >/dev/null || export PATH=\"$PYENV_ROOT/bin:$PATH\"' >> ~/.bashrc \
&& echo 'eval \"$(pyenv init -)\"' >> ~/.bashrc \
&& source ~/.bashrc \
ruby-spacy の案内にしたがう
ruby-spacy の README に従い、enable-shared オプションを有効にし、spaCy と 言語モデルをインストールしていきます。今回は言語モデルとして ja_core_news_sm を導入しています。
&& export CONFIGURE_OPTS=\"--enable-shared\" \
&& pyenv install 3.10.6 \
&& pyenv global 3.10.6 \
&& pip install spacy==3.6.0 \
&& pip install ginza==5.1.3 \
&& python -m spacy download ja_core_news_sm \
"]
rails console で確認する
docker build と docker up が完了したら、rails コンテナに入り、Rails コンソールを利用して、ruby-spacy 経由で、spaCy を呼び出せることを確認します。
サンプルコードは ruby-spacy の README に記載されているものです。
rails c
require "ruby-spacy"
require "terminal-table"
nlp = Spacy::Language.new("ja_core_news_sm") # インストールした言語モデルを指定
doc = nlp.read("任天堂は1983年にファミコンを14,800円で発売した。")
headings = ["text", "lemma", "pos", "tag", "dep"]
rows = []
doc.each do |token|
rows << [token.text, token.lemma, token.pos, token.tag, token.dep]
end
table = Terminal::Table.new rows: rows, headings: headings
puts table
さいごに
spaCy を呼び出す ruby-spacy の導入を行いました。
また、Python を呼び出せる PyCall の存在も今回初めて知ったので今後使ってみたいと思います。
参考
その他学習の参考になったページ
-
という認識です。間違ってたら教えてください。。。 ↩︎
Discussion