OSS に Contribute しました
簡単な変更でしたが、OSS に Contribute しました。
内容と所感
内容はドキュメントに favicon.ico を追加しただけという…笑
実は OSS にコミットという点で言えば、初めてのコミットだった。
しかも、今日初めて読むプロジェクトに Sphinx という不慣れなフレームワークでの修正。
なので、小さな変更だがすごく達成感があり、気持ちとしては晴れ晴れしている。
元々は OSS にコミット、さらに言えば、機械学習系の OSS にコミットしていきたいという気持ちがあった。
なので、今回の変更は小さいけれど、これをきっかけにもっとコミットしていきたいと思っている。
Good first issue
そもそもなんで OSS に Contribute したいと思っていたのに、できなかったか。
正直、OSS にコミットしたくていろいろ資料よんだけど、
結局のところ、何すれば良いのか、というのがわからなかったというのが大きかった。
そんななか、この資料を読んだ。
OSS のリポジトリをみる時に、ざっくり issue を読んでいた。
しかし、上記の資料を見た時、どの issue に注目すれば良いかわかったので、
今回のような些細だが Contribute するきっかけを得られたと思っている。
今後
引き続き、 blueoil
は引き続き注目して ML の範囲で Contribute できるように挑戦していきたい。
Python で標準出力をリアルタイムに出力する方法
Jenkinsで実行ログをリアルタイムで確認したい
Jenkis ではジョブの実行ログをリアルタイムに確認することができる。
しかし、 ( Python ではスクリプト言語のため)、処理を高速化するために標準出力をバッファしてまとめて出力する。
なので、 Jenkins でジョブの動作を確認する場合、まとまった単位で標準出力のログが出力される。
毎日、定時に実行するようば定例バッチの場合はそれでもいいが、
開発途中の場合、リアルタイムにログがみたい場合があると思う。
そのような場合の忘備録を残す。
Pythonでリアルタイムに標準出力するには
Pythonでは使うバージョンによって、即時標準出力する方法が異なる。
Python3.3以降
これは有名な方法だが、 flush
オプションを True
で設定する。
print("プリントする文言", flush=True)
Python3.3より前 + Python2.X 系
2020年には Python 2系はメンテされなくなると思うが、一応載せておく。
sys.stdout.flush()
を使うことで flush される。
import sys print("プリントする文言") # Python 2系なら # print "プリントする文言" sys.stdout.flush()
いちいち仕込むべきなのか
冒頭で書いたように、バッチの性質によって必ずしも常に flush する必要はないと思う。
しかし、リアルタイムに出力させたい場合に備えて、 flush を仕込む方針を取ると非常に厄介だと思う。
例えば、今ブログを書きながらで思いつく方法としては、
バッチ実行時に flush をするかどうかの判定を挟む方法だ。
例えば、環境変数に flush_flg を仕込んでそれを使って判定する、のような処理。
この場合、個人で開発している場合は統一できるかもしれないが、時間の経過と共に忘れてしまったりするし、
チームで開発している場合、人それぞれの書き方や基準があやふやになって推奨できない。
また、そのために共通関数化するのもたかが標準出力のためにバカらしい。
不用意な変数を仕込むのでバグの温床にもなりかねない。
なので、できるならシンプルな形式に留めるほうが良いと思う。
Python のオプションを使う
Python のオプションに -u
オプションがある。
python -h
とすると用途が見られる。
以下に出力を抜粋する。
ちなみに自分が使っているバージョンは Anaconda の Python 3.6.5 である。
$ python -V Python 3.6.5 :: Anaconda, Inc. $ python -h ... (抜粋) -u : force the binary I/O layers of stdout and stderr to be unbuffered; stdin is always buffered; text I/O layer will be line-buffered; also PYTHONUNBUFFERED=x ...
とある。
要約すると、標準出力とエラー出力を強制的にバッファしないようにする。とのこと。
つまり、このオプションを使えば flush と同じことができる。
なので、例えば開発環境などで随時標準出力を見たい場合は、 Jenkins のジョブ実行スクリプト画面で
python -u <実行するスクリプト>
すればよいし、そのまま定例実行に切り替えるときは、
python <実行するスクリプト>
とするだけでよい。
FYI
Python2 count down
ElasticSearch に Bulk Insert をする
以前の記事でも記載したが、本業のプロジェクトで ElasticSearch を利用したシステムの構築をしようとしている。
Docker で作ったデモ環境では、 Embulk での bulk insert ができていた。
しかし、 AWS Elasticsearch Service では embulk-output-elasticsearch がサポート外のため、利用できなかった。
なので、別の手段で bulk insert を実装した。
Python の ElasticSearch Client を使う
コードは Python をベースで利用する。
なので、 ElasticSearch の Client として、
Python Elasticsearch Client — Elasticsearch 6.3.0 documentation
を利用する。 インストールは、
pip install elasticsearch
でインストールできる。
基本的には、インストールする Client のバージョンは、 ElasticSearch インスタンスのメジャーバージョンに合わせて利用するのが推奨されていそう。
なので、例えば ElasticSearch の 5.X.X を利用している場合、インストールする Client は
pip install elasticsearch>=5.0.0,<6.0.0
でインストールすることで、5系の中の最新バージョンの Client をインストールすることができる。
bulk insert
ElasticSearch に bulk insert する場合、
helpers の中の bulk method を利用することで bulk insert が実装できる。
参考として、以下に 1000 件のデータに対して bulk insert するコード例を載せる。
from elasticsearch import Elasticsearch, helpers es = Elasticsearch(host='localhost', port=9200) data = [] for i in range(1000): doc = {'hoge': 'foobar'} data.append( {'_index':'hoge-index', '_type':'hoge-index-type', '_source':doc} ) helpers.bulk(es, data)
100件などの単位で分割して insert したい場合、 for ループ内で適当な件数で bulk method を呼び出すようにすれば良い。
感想
Embulk で insert するのもとても楽だったが、Elasticsearch の helpers を利用して簡単に bulk insert が実装できた。
Elasticsearch の helpers には他にも便利な method が多くあるのでやりたいことを helpers 内の method で実現できるかを考えたほうが良さそうだなと思った。
DockerCompose で ES + Kibana 環境を構築しました!
前回の記事で ElasticSearch と Kibana の両方を Docker で起動する方法を紹介した。
しかし、 Docker の Link などを使って管理していたので、手動で管理するものがたくさんあり、大変だった。
なので、今回は Docker Compose を利用する方法を紹介する。
Docker Compose
今回の ElasticSearch と Kibana のように、複数のコンテナを連携して管理する方法をオーケストレーションという。
このオーケストレーションの方法はいくつかあり、今話題の Kubernetes などがあるが、今回はその一つの Docker Compose を使った。
Kubernetes の場合、複数の pods 間で service discovery を行ってくれる機能や pods のスケーリングなどの機能がある。
Docker Compose は kubernetes のような複雑な機能はないが、一つのファイルで複数のコンテナを管理できる。
なので、今回のように実験的に環境を作ることに関して言えば、 Kubernetes は必要過多なサービスになると判断して、
Docker Composeでコンテナのビルド、起動、シャットダウンなどを一括で管理できるメリットを取った。
ES + Kibana with Docker Compose
複数のファイルにまたがるので、 Github 上にリポジトリを作成しました。
ElasticSearch も Kibana も 6.4.0
を利用している。
ElasticSearch
日本語の文章を解析するためには形態素解析器を使って文章を品詞ごとに分解する必要がある。
そのため、 ElaskticSearch の Dockerfile では、形態素解析器に elasticsearch-analysis-kuromoji のパッケージをダウンロードした。
パッケージをダウンロードするために標準入力でインストールを許可する入力が必要となる場合がある。
Docker でイメージをビルドする際、標準入力できないので、今回は echo y
を利用して y
を標準入力している。
docker-compose.yml
このファイルの中で ElasticSearch と Kibana のイメージを管理している。
yaml
ファイル形式になっており、読み方は key-value として読んでいくと良いかと思う。
詳しくは、リファレンス
Compose ファイル・リファレンス — Docker-docs-ja 17.06.Beta ドキュメント
を読むのがいいと思う。
このリポジトリ内で注意するべき点がいくつかあるので、以下にメモしておく。
build
4行目と24行目に build
key がある。
これは value 名のディレクトリに存在する Dockerfile をビルドする。
なので、ここの value は docker-compose.yml
がある構築用コンテクストのパスを指定する必要がある。
そして、各行の次の行で container-name
でコンテナ名をつけている。こちらはときにディレクトリとは関係ないので自分が好きな名前をつければ良い。
ちなみに build 済みのイメージを利用することもでき、 image
を利用し、イメージ名を指定すればよい。
詳しくは、
Compose ファイル・リファレンス — Docker-docs-ja 17.06.Beta ドキュメント
に記載されている。
volumes
Docker は基本的にコンテナ内のデータは揮発性であり、毎回コンテナを起動すると初期状態で起動される。
つまり、前回 ElasticSearch に入力したデータは残らない。
データを保存したい場合、 docker の data volume として保存する必要がある。
今回は、 docker-compose.yml
の30行目で data volume をローカルの docker 環境に構築し、利用するようにしている。
なので、真新しい環境を構築するためには、コンテナの再構築・再起動以外に、この volume を削除する必要がある。
辞書のマウント
kuromoji で形態素解析を行うにあたり、カスタマイズした辞書を利用するように template
に記載している。
そのため、 docker-compose.yml
の8行目で辞書をマウントしている。
この辞書は csv
形式で1行が一つの形態素解析の結果を表しており、
本文中の形式,形態素解析の結果,ヨミ,品詞
の形にしなければならない。
まとめ
今回は DockerCompose を利用した ES + Kibana 環境を構築した。
ES 環境で日本語の解析を試しに行いたい場合は、お手軽に試せるので是非活用してください。
わからないところなどはコメントや Github の issue にお願いします!
Docker で ElasticSearch + Kibana 環境を構築してみた。
本業で ElasticSearch を使うシステム開発を行うことになった。 機能の POC を確認するため、 Docker で ElasticSearch と Kibana を起動することにした。 自分は Docker for Mac を利用している。
Note
Linux 環境や Mac ユーザだけど Docker for Mac を利用されていない方は、各コンテナに接続確認する場合の localhost
部分を適宜 $(docker-machine ip <machine-name>)
に置換してください。
ネットワーク
複数のコンテナを利用するので、 Bridge ネットワークでコンテナ同士をつなぐ。
docker network create elasticsearch --driver bridge
コンテナ
ElasticSearch コンテナ起動
docker run -d \ -e "http.host=0.0.0.0" \ -e "transport.host=127.0.0.1" \ -e "xpack.security.enabled=false" \ -e "xpack.monitoring.enabled=false" \ -e "xpack.watcher.enabled=false" \ -e "xpack.graph.enabled=false" \ -e "xpack.ml.enabled=false" \ -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \ -p 9200:9200 \ -p 9300:9300 \ --name elasticsearch \ --network="elasticsearch" \ docker.elastic.co/elasticsearch/elasticsearch:6.4.0
ElasticSearch コンテナが起動しているかを確認するには、
curl localhost:9200
でレスポンスを見てみる。 以下、みたいなレスポンスが返ってきた。
{ "name" : "MAzzjYI", "cluster_name" : "docker-cluster", "cluster_uuid" : "wEvHKuikQyeE1Ca7UogUZQ", "version" : { "number" : "6.4.0", "build_flavor" : "default", "build_type" : "tar", "build_hash" : "595516e", "build_date" : "2018-08-17T23:18:47.308994Z", "build_snapshot" : false, "lucene_version" : "7.4.0", "minimum_wire_compatibility_version" : "5.6.0", "minimum_index_compatibility_version" : "5.0.0" }, "tagline" : "You Know, for Search" }
Kibana コンテナ起動
docker run -d \ --name kibana \ -p 5601:5601 \ -e "ELASTICSEARCH_URL=http://elasticsearch:9200" \ -e "xpack.graph.enabled=false" \ -e "xpack.security.enabled=false" \ -e "xpack.ml.enabled=false" \ --network="elasticsearch" \ docker.elastic.co/kibana/kibana:6.4.0
Kibana が起動しているかはブラウザでチェックする。
open http://localhost:5601
できた。
所管
恐らく本当は k8s とか何かしらのオーケストレーションツールを使って管理するのが望ましい気がする。 本当は kubernetes を使ってやりたいけど、一旦、次は docker-compose を使ってみようかな。
Prestoで2つの配列を同時に行に変換する
最近、Treasure Dataに触る機会が増えた。 その中で1行に2つのカラムにそれぞれコンマ区切りのデータがあり、それを行展開するのにハマったポイントがあったのでメモしておく。 そして長くなるので、最初に結論を載せておく。
どういうデータか
以下のようなデータ形式を想定している。
column1 | column2 |
---|---|
a,b,c | 1,2,3 |
そして、このデータを以下のデータ形式に展開する。
column1 | column2 |
---|---|
a | 1 |
b | 2 |
c | 3 |
前提としては、column1もcolumn2も同数の要素数が含まれていることとする。
どうするか
以下のように CROSS JOIN UNNEST
の中に複数の split
関数を入れる。
SELECT clmA, clmB FROM tbl1 CROSS JOIN UNNEST( split( tbl1.column1, ',' ), split( tbl1.column2, ',' ) ) AS t(clmA, clmB);
これにより上記のような展開ができます。
どういうことをしたか
ググってみると、Prestoでカンマ区切りのデータを展開した記事はよく出てくる。
これを見たときは、『そっか、じゃあ CROSS JOIN UNNEST をもう1段挟めばいいのか』
と安易に考えてしまいました。
ざっと以下のような感じ。()
SELECT t1.clmA, t2.clmB FROM tbl1 CROSS JOIN UNNEST( split( tbl1.column1, ',' ) AS t1(clmA) CROSS JOIN UNNEST( split( tbl1.column2, ',' ) ) AS t2(clmB);
そうすると以下のような結果が得られた。
column1 | column2 |
---|---|
a | 1 |
a | 2 |
a | 3 |
b | 1 |
b | 2 |
b | 3 |
c | 1 |
c | 2 |
c | 3 |
まさに CROSS JOIN。
なので、次は、『そっか、じゃあWITH句をつかって、一回テーブルとして展開してから、ID付きで展開すればうまくいくんじゃね?』 と思い、やりました。 が、やはり上記のようなクエリの結果になってしまいました。 なかなか奥深いですね…
勉強会に登壇してきました。
機械学習エンジニアとして副業をしていることもあり、FindyでCTOしている @ma3tk からのお誘いもあり登壇してきました。
engineer-parallel-work.connpass.com
発表資料はこちら。
副業をしていて感じていることを話してきた。
まだ初めて数ヶ月だが、いい振り返りになった。
視座を高めるために知識の input だけでなく、 output を行い経験値を増やすこと。
そのサイクルを回すことで自身の視座を高くしていく努力をしていくこと。
それを再認識できた発表だった。
懇親会であった質問
ものすごくありがたいことに参加者から、『発表が良かった』と言ってくださる方が多くいらっしゃった。
その方々といろいろ話すことができたので、質問と自分の考えをまとめておく。
始め方はどうされたのですか?
僕の場合、スライドに載せているFISMという会社のCTOをしている @kzkohashi と同僚であり、
そのつながりで始めることができた。
元々興味はあったが、なかなか始める決意がつかなかったが、とてもいいきっかけをくれたと思っている。
いいなと思っている会社があり、そこで副業できればなと思っていますが、どう思いますか?
副業として受け入れてくださるのであれば、積極的に取り組めばいいと思っている。
正直、外から見ていていいなーと思う会社はいっぱいある。
ただ、入ってみて、あれ?と思うこともある。
そうなってしまったときに、現職を退職してまでって後悔がすごく、これが現職にとどまり続ける要因の一つなのかと思う。
そういう意味では副業として参加し、正社員と肩を並べて働いてみて、直に会社の文化に触れることはいいスタートの切り方なのではないだろうか?
そして、思ったより肌に合わないと判断できた場合は、信頼を稼いでつながりを構築することに専念すればいいと思っている。
アウトプットの仕方どうしていますか?
データ分析の場合、データを分析して最後の可視化と考察の部分が重要だと思っている。
結局のところ期待されているのはアウトプットであり、例えば、
- 必要なタスクに対して実現可能なのか不可能なのか
- 不可能な場合、だいたい手段はあるのかもっと作業を進めれば可能性はでてくるのか
などが期待されているのではないだろうか。
データ分析といえばデータの前処理が重要であることは重々承知している。
しかし副業だと、発注者側にデータ分析を理解してくれる人がいない場合、
最初から前処理をコツコツやっていても、「何してるんですか?」ってなると思う。
それよりも、
「既存のデータを使ってこうしました。ここの部分の改善をすることで精度があがると思うので、次にここを着手します。」
と道筋を見せて上げたほうが納得感が得られるし、自分自身必要な処理が見えてくるはずである。
なので、アウトプットを細かく見せることはすごく重要だと考えている。
ポートフォリオはどうされていますか?
ここで悩んでいる人が多かった印象だった。
正直、自分は知り合いの相談から副業がスタートしているので、ポートフォリオとかを考えなくても良かった。
しかし、もっといろんな会社の案件を触ってみたいなと思ったときに、ポートフォリオをつくらないとなとなった。
恐らく、ポートフォリオとして上がるのは、「エンジニアならgithubだろ!」というのが定石なのではないだろうか。
その声がすごく多かったし、それには賛成である。
しかし、機械学習のエンジニアはデータがないとなかなかアピールポイントがないので、
githubにコードを上げることは難しいのではないかと思う。
そこで僕はブログもセットでポートフォリオを組むといいのではないかと思っている。
データ前処理の中で得られた気づきは、大抵、gistやgithubに上げるほどではない。
だが、その一部をブログとして残すことで自分がどの程度データに触れているのかというのをアピールすることに繋げられる。
積もり積もればれっきとした履歴書であるし、コード以外の考えをアピールすることができるのでよいと思う。
ブログのアウトプットについては @kakakakku のブログに対する考え方は参考になるので、
一度は読まれたほうがいいと思う。
最後に
今回は本当に貴重な会で登壇させていただけた。 今後もいろんな勉強会に登壇できるよう、自分の成長に繋げられるように input/output を引き続きがんばろう。