中野智文のブログ

データ・マエショリストのメモ

luigi で パラメータで与えられた日付をtimedelta を使って増減させたい

背景

luigi にて、パラメータで与えられた日付に対して、固定日分前(例えば一週間など)を求めたい。

from datetime import timedelta
import luigi


class MyTask(luigi.Task):
    date = luigi.DateParameter()
    start_date = date - timedelta(days=7)

と書くと、次のようなエラー。

unsupported operand type(s) for -: 'DateParameter' and 'datetime.timedelta'

確かに、date はDateParameterなので、エラーの通りなのだが。

stackoverflow.com

での、回答者のコメントでは、 “DateParameter returns a value that is a python date. ” といってるので、半分信じてしまった。 ちなみにこのQAは役立たない。なぜならデフォルト値の計算をしたいわけではないから。

クラス変数とインスタンス変数の違い

関数の中の、self.date はクラス変数と思っていたが、間違いだ。これはインスタンス変数だ。

厄介なことに、 self.date の表記は python 的にはクラス変数でもインスタンス変数にもなるらしい。 インスタンス変数として「初期化」すれば、クラス変数は隠蔽され以後インスタンス変数として扱われる。 ただ参照するだけならば、クラス変数として扱われる。

d.hatena.ne.jp

結局、self.dateインスタンス変数ということは、親のクラスのインスタンス初期化の処理などで、クラス変数の設定通りの動作が行われ、self.date というインスタンス変数に値がセットされたのだろう。

解決

from datetime import timedelta
import luigi


class MyTask(luigi.Task):
    date = luigi.DateParameter()

    def requires(self):
        start_date = self.date - timedelta(days=7)    

もし、固定の日でなく、パラメータで与えたいのであれば、TimeDeltaParameter を使おう。

http://luigi.readthedocs.io/en/stable/api/luigi.parameter.html#luigi.parameter.TimeDeltaParameter

luigi の RangeDaily をコードで使う

背景

範囲日の繰り返し DateIntervalParameter

指定した範囲の日の処理を指定したい場合は、チュートリアルには次のようなコード例がある。

http://luigi.readthedocs.io/en/stable/example_top_artists.html#step-1b-running-this-in-hadoop

    def requires(self):
        return [StreamsHdfs(date) for date in self.date_interval]

この date_interval はこれ以降説明など出てこない。もっと細かい仕様を調べたい場合は、 DateIntervalParameter のドキュメントを読む必要がある。

http://luigi.readthedocs.io/en/stable/api/luigi.parameter.html#luigi.parameter.DateIntervalParameter

何かちょっとドキュメントとして読みにくい。また DateIntervalParameter はタスクのパラメータであるので、 本当にほしいのは、渡された別のパラメータから日付の範囲を生成する DateInterval のような気がするのだが、

http://luigi.readthedocs.io/en/stable/api/luigi.date_interval.html#luigi.date_interval.DateInterval

ドキュメントは徐々に何も書かれていなくなってくる。

範囲日の繰り返し RangeDaily

ところで、luigi コマンド上で上記のような範囲指定を行いたい場合コードが書けないので、タスクのコードをDateParameterから、DateIntervalParameterに変えるとか、 それを呼び出す範囲日指定のラッパータスクを作成しなければならない。 そこでコマンドレベルでも特にコードを書かずに上記のような範囲の実行を行う一般的なラッパータスク RangeDaily が用意されている。

Luigi Patterns — Luigi 2.6.2 documentation

luigi --module all_reports RangeDaily --of AllReportsV2 --start 2014-10-31 --stop 2014-12-25

本題

RangeDaily はコードでも使えるよ、という話。RangeDaily であればドキュメントは割とあるのと、広範囲を誤って指定しても実行されないような仕組みがあるので、 そういった意味で比較的安全に範囲日を指定できるというメリットがある。

コード例

import luigi
from luigi.tools.range import RangeDaily
from my_sub_task import MySubTask

class MyTask(luigi.Task):
    date = luigi.DateParameter()
    start_date = luigi.DateParameter()
    opt1 = luigi.Parameter()
    opt2 = luigi.Parameter()

    def requires(self):
        return [RangeDaily(of=MySubTask,
                           start=self.start_date,
                           stop=self.date,
                           task_limit=100,
                           days_back=200,
                           of_params={"opt1": self.opt1,
                                      "opt2": self.opt2})]

関数の outputrun は必要。

task_limit days_back は安全のためのオプションで、それぞれデフォルトでは 50 と100 に設定されている。 詳しくは次で確認できる。

http://luigi.readthedocs.io/en/stable/api/luigi.tools.range.html#luigi.tools.range.RangeBase

http://luigi.readthedocs.io/en/stable/api/luigi.tools.range.html#luigi.tools.range.RangeDailyBase

of_paramsdate 以外のパラメータを渡すための、dict。 注意しなくてはいけないのは、 date型などを直接この op_params の value に入れると、JSON 化できないというエラーとなる。 文字列に変換してから入れる必要があるが、文字列に変換すると今度は DateParameter の方が日付型に変換してくれないというオチ。 (そんなときは無理してこの RagenDaily を使う必要はない)

コマンドライン

コマンドラインで呼び出す場合は次のようになる。

luigi --module my_task MyTask --start-date 2017-07-22  --date 2017-07-25 --opt1=aaa opt2=bbb --workers=5

--workers のオプションはここに来る。(RangeDailyのオプションではないので)。 また 今回のコードにする話に限ったことではないが、--stop はその日を含まないので注意。

(おまけ)その日を含むコードを実行

require にその日を追加する方法。

    def requires(self):
        return [MySubTask(date=self.date,
                          opt1=self.opt1,
                          opt2=self.opt2),
                RangeDaily(of=MySubTask,
                           start=self.start_date,
                           stop=self.date,
                           task_limit=100,
                           days_back=200,
                           of_params={"opt1": self.opt1,
                                      "opt2": self.opt2})]

stop に一日後を指定する方法。

timedelta を、 import する必要がある。

from datetime import timedelta # (←追加)
...
(中略)
...

    def requires(self):
        stop_date = self.date + timedelta(days=1)   
        return [RangeDaily(of=MySubTask,
                           start=self.start_date,
                           stop=stop_date,
                           task_limit=100,
                           days_back=200,
                           of_params={"opt1": self.opt1,
                                      "opt2": self.opt2})]

Julia の GaussianProcesses を jupyter notebook で表示する

背景

そろそろ jupyter notebook を使いたくなってきた。

前回、Gaussian Processes のライブラリを入れるところまではできたので(下記参照) nakano-tomofumi.hatenablog.com

今回は、jupyter notebook 上でグラフを表示するところまでやってみる。

IJulia インストー

申し訳ない。既にインストール済みだったのか、さっくりできた。

julia> Pkg.add("IJulia")
julia> Pkg.update()
julia> using IJulia
julia> notebook()

jupyter 上で GaussianProcesses ライブラリを使う。

以下は jupyter notebook 上での実行の話。

using GaussianProcesses

INFO: Recompiling stale cache file /Users/nakanotomofumi/.julia/lib/v0.5/Optim.ji for module Optim.

LoadError: ArgumentError: Module Klara not found in current path.
Run `Pkg.add("Klara")` to install the Klara package.
while loading /Users/nakanotomofumi/.julia/v0.5/GaussianProcesses/src/GaussianProcesses.jl, in expression starting on line 2

もしかしてパスの問題かもしれないが、素直に入れてみる。

Pkg.add("Klara")

問題なく入った。

本家のReadme.md が変更されている。

よく読むと、jupyter notebook へ移ったとある。つい最近変更したようだ。 なるほど、前回の例は、何が実行コマンドで何が出力か分かりにくかった。 しかしjupyter notebookへのリンクをクリックしても、当該ファイルへは飛ばない…

どうやら、下記らしい。

github.com

あとはこの通りに実行して…。

pyplot の場所でエラーが…。あれ?本家の jupyter notebookもエラーだし…。

エラーでなくなってからマージして欲しい…。

BigQuery のスキーマを bq コマンドで使う形式で表示する jq

背景

ある日の空テーブルを作成しようとしたが、スキーマの文字列が必要になった。手作業だとミスが起こるから、コマンドが欲しい。

jq コマンド

スキーマ文字列を取得

$ bq show --format prettyjson <既存のBQのテーブル名> | jq -r '.schema[] | map(.name+":"+.type) | join(",")

一気に空テーブル作成

$ bq mk --schema `bq show --format prettyjson <既存のBQのテーブル名> | jq -r '.schema[] | map(.name+":"+.type) | join(",")'` -t <空のBQのテーブル名>

Julia の GaussianProcesses のライブラリを入れようとする (2)

前回はこちら

nakano-tomofumi.hatenablog.com

Optim の仕様変更への対応

問題のエラーは、次のようなもの

julia> optimize!(gp)
ERROR: MethodError: no method matching set_params!(::GaussianProcesses.GP, ::Float64; noise=true, mean=true, kern=true)
Closest candidates are:
  set_params!(::GaussianProcesses.GP, ::Array{Float64,1}; noise, mean, kern) at /Users/xxxxx/.julia/v0.5/GaussianProcesses/src/GP.jl:275
  set_params!{K<:GaussianProcesses.Kernel}(::GaussianProcesses.Masked{K<:GaussianProcesses.Kernel}, ::Any) at /Users/xxxxxx/.julia/v0.5/GaussianProcesses/src/kernels/masked_kernel.jl:55 got unsupported keyword arguments "noise", "mean", "kern"

これに対して、既に修正が行われている模様:

github.com

そして実行。

julia> Pkg.checkout("GaussianProcesses")
INFO: Checking out GaussianProcesses master...
INFO: Pulling GaussianProcesses latest master...
INFO: No packages to install, update or remove

おや?変化なし。

パッケージが最新版を見ていない模様。

julia> Pkg.status()
4 required packages:
 - GaussianProcesses             0.4.0+             master

statusを見ると、master を見ていることになっている。再コンパイルが必要?

最新のブランチを反映させる方法を知らないため、一旦 exit() し、再び julia を起動し、using で呼び出し。

次は、plot(gp) でエラーだ。

なので、 一旦、Pkg.free("GaussianProcesses") で元に戻す。

そして、再び exit して、その後、Pkg.checkout("GaussianProcesses") を実行。

plot(gp) でエラー出ず。

f:id:nakano-tomofumi:20170710191518p:plain

そして、

julia> optimize!(gp)
Results of Optimization Algorithm
 * Algorithm: Conjugate Gradient
 * Starting Point: [-1.0,0.0,0.0,0.0]
 * Minimizer: [-3.506712993556627,-0.080606913136668, ...]
 * Minimum: -3.600592e+00
 * Iterations: 19
 * Convergence: false
   * |x - x'| < 1.0e-32: false
   * |f(x) - f(x')| / |f(x)| < 1.0e-32: false
   * |g(x)| < 1.0e-08: false
   * f(x) > f(x'): true
   * Reached Maximum Number of Iterations: false
 * Objective Function Calls: 69
 * Gradient Calls: 50

キタ━━━━(゚∀゚)━━━━!!

f:id:nakano-tomofumi:20170710191527p:plain

まとめ

一応、Juliaのライブラリ GaussianProcesses で超パラメータの最適化は出来た。 しかし、他のライブラリ(Optim)の勝手なバージョンアップ等もあって、うまく動くことは保証されない。 今後も勝手なバージョンアップがありそうだから、ビジネス用途には全く向いていない。あくまで遊び(教育用途)レベルにとどめておくべき。

Julia の GaussianProcesses のライブラリを入れようとする

背景

連続腕バンディット(それもトンプソンサンプリング)をやってみようと思った。 ベルヌーイ分布のトンプソンサンプリングは、ベータ分布の乱数生成だけが肝だが、ライブラリを使ったり、最悪Cの関数を参考にしながら自分で実装すれば特に問題はない。 ところが、ガウス過程の乱数生成は、まずガウス過程の分布を計算するのが一苦労なのと、その分布から乱数を生成する必要があり、またこれも面倒である可能性がある。 とりあえず、フォン・ノイマンの棄却法とか、分布さえ求められればなんとかなりそうな気配はあるので、ガウス過程の分布を求めるところからスタートする。

Julia の GaussianProcesses のライブラリを入れる。

なぜ Julia かといえば、特に理由はないが、python はすでに多くやられていて、特に問題なくインストールとかできそうだから。

github.com

早速、README.md どおりインストールを開始する。

julia> Pkg.add("GaussianProcesses")
INFO: Cloning cache of Distances from https://github.com/JuliaStats/Distances.jl.git
INFO: Cloning cache of GaussianProcesses from https://github.com/STOR-i/GaussianProcesses.jl.git
INFO: Cloning cache of LineSearches from https://github.com/JuliaNLSolvers/LineSearches.jl.git
INFO: Cloning cache of Optim from https://github.com/JuliaNLSolvers/Optim.jl.git
INFO: Cloning cache of PositiveFactorizations from https://github.com/timholy/PositiveFactorizations.jl.git
INFO: Cloning cache of ScikitLearnBase from https://github.com/cstjean/ScikitLearnBase.jl.git
INFO: Installing Distances v0.4.1
INFO: Installing GaussianProcesses v0.4.0
INFO: Installing LineSearches v0.1.5
INFO: Installing Optim v0.7.8
INFO: Installing PositiveFactorizations v0.0.4
INFO: Installing ScikitLearnBase v0.3.0
INFO: Package database updated
INFO: METADATA is out-of-date — you may not have the latest version of GaussianProcesses
INFO: Use `Pkg.update()` to get the latest versions of your packages

julia> Pkg.update()
INFO: Updating METADATA...
INFO: Updating cache of Combinatorics...
INFO: Updating cache of StatsBase...
INFO: Updating cache of Roots...
INFO: Updating cache of PolynomialFactors...
INFO: Updating cache of Distributions...
INFO: Updating cache of Rmath...
INFO: Updating cache of Compat...
INFO: Updating cache of PDMats...
INFO: Computing changes...
INFO: Cloning cache of IterTools from https://github.com/JuliaCollections/IterTools.jl.git
INFO: Upgrading Combinatorics: v0.4.0 => v0.4.1
INFO: Upgrading Compat: v0.25.2 => v0.26.0
INFO: Installing IterTools v0.1.0
INFO: Upgrading PDMats: v0.6.0 => v0.7.0
INFO: Upgrading Rmath: v0.1.6 => v0.1.7
INFO: Upgrading Roots: v0.3.1 => v0.4.0
INFO: Upgrading StatsBase: v0.15.0 => v0.17.0
INFO: Removing Iterators v0.3.1
INFO: Removing PolynomialFactors v0.0.5
INFO: Removing Primes v0.1.3
INFO: Building Rmath

ちょっと GaussianProcesses は古いとでた。

ではヘルプを見てみよう。

julia>

help?> GP
search: get_bigfloat_precision gperm CachingPool log1p logspace getpid graphemes isgraph getipaddr set_bigfloat_precision with_bigfloat_precision

Couldn't find GP
Perhaps you meant gc, !, !=, $, %, &, *, +, -, .%, .*, .+, .-, ./, .<, .>, .\, .^, .÷, .≠, .≤, .≥, /, //, :, <, <:, <<, <=, ==, =>, >, >=, >>, I, \, ^, cd, cp, e, eu, fd, im, in, lq, lu, mv, pi, qr, rm, |, |>, ~, ×, ÷, γ, π, φ, ∈, ∉, ∋, ∌, √, ∛, ∩, ∪, ≈, ≉, ≠, ≡, ≢ or ≤
  No documentation found.

  Binding GP does not exist.

おっと、いきなりこれか。using してないからじゃないの?

julia> using GaussianProcesses
INFO: Precompiling module Optim.
INFO: Recompiling stale cache file /Users/xxxxxx/.julia/lib/v0.5/PDMats.ji for module PDMats.
INFO: Precompiling module Distances.
INFO: Precompiling module ScikitLearnBase.

したら、

help?> GP
search: get_bigfloat_precision GP gperm CachingPool log1p logspace getpid graphemes isgraph getipaddr GaussianProcesses set_bigfloat_precision with_bigfloat_precision
...

でるじゃん。

サンプルを試す。

julia> using PyPlot, GaussianProcesses
ERROR: ArgumentError: Module PyPlot not found in current path.
Run `Pkg.add("PyPlot")` to install the PyPlot package.
 in require(::Symbol) at ./loading.jl:365
 in require(::Symbol) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?

やはり。

julia> Pkg.add("PyPlot")
....

うーん、conda とか、numpy とかがインストールされていくのを見ると、julia でやっている意味はあるのかって一瞬思う。 (一瞬だったが)

その後 README.md にあるコマンドを順に実行する。

次は出力なので気をつける。

  Dim = 1
...
julia> plot(gp)

f:id:nakano-tomofumi:20170707171604p:plain

それっぽいのが出てきた。

カーネル関数の超パラメータの調整

カーネル関数には事前にセットされた数々の超パラメータがある。この超パラメータを調整する方法として、第二種の最尤推定法とよばれる方法がある。 PRML本だと、ガウス過程の第二種の最尤推定法は「6.4.3 超パラメータの学習」で触れられている。

では、この超パラメータの調整をREAEME.mdどおり optimize!(gp) で行ってみよう。

julia> optimize!(gp)   #Optimise the hyperparameters
WARNING: DifferentiableFunction(args...) is deprecated, use OnceDifferentiable(args...) instead.
 in depwarn(::String, ::Symbol) at ./deprecated.jl:64
 in DifferentiableFunction(::Function, ::Vararg{Function,N}) at ./deprecated.jl:50
 in #get_optim_target#20(::Bool, ::Bool, ::Bool, ::Function, ::GaussianProcesses.GP) at /Users/XXXX/.julia/v0.5/GaussianProcesses/src/optimize.jl:74
 in (::GaussianProcesses.#kw##get_optim_target)(::Array{Any,1}, ::GaussianProcesses.#get_optim_target, ::GaussianProcesses.GP) at ./<missing>:0
 in #optimize!#19(::Bool, ::Bool, ::Bool, ::Optim.ConjugateGradient{Void,Optim.##29#31,LineSearches.#hagerzhang!}, ::Array{Any,1}, ::Function, ::GaussianProcesses.GP) at /Users/XXXX/.julia/v0.5/GaussianProcesses/src/optimize.jl:17
 in optimize!(::GaussianProcesses.GP) at /Users/XXXX/.julia/v0.5/GaussianProcesses/src/optimize.jl:17
 in eval(::Module, ::Any) at ./boot.jl:234
 in eval(::Module, ::Any) at /Applications/Julia-0.5.app/Contents/Resources/julia/lib/julia/sys.dylib:?
 in eval_user_input(::Any, ::Base.REPL.REPLBackend) at ./REPL.jl:64
 in macro expansion at ./REPL.jl:95 [inlined]
 in (::Base.REPL.##3#4{Base.REPL.REPLBackend})() at ./event.jl:68
while loading no file, in expression starting on line 0
ERROR: MethodError: no method matching set_params!(::GaussianProcesses.GP, ::Float64; noise=true, mean=true, kern=true)
Closest candidates are:
  set_params!(::GaussianProcesses.GP, ::Array{Float64,1}; noise, mean, kern) at /Users/XXXX/.julia/v0.5/GaussianProcesses/src/GP.jl:275
  set_params!{K<:GaussianProcesses.Kernel}(::GaussianProcesses.Masked{K<:GaussianProcesses.Kernel}, ::Any) at /Users/XXXX/.julia/v0.5/GaussianProcesses/src/kernels/masked_kernel.jl:55 got unsupported keyword arguments "noise", "mean", "kern"
 in #optimize!#19(::Bool, ::Bool, ::Bool, ::Optim.ConjugateGradient{Void,Optim.##29#31,LineSearches.#hagerzhang!}, ::Array{Any,1}, ::Function, ::GaussianProcesses.GP) at /Users/XXXX/.julia/v0.5/GaussianProcesses/src/optimize.jl:20
 in optimize!(::GaussianProcesses.GP) at /Users/XXXX/.julia/v0.5/GaussianProcesses/src/optimize.jl:17

グフ… 色々古いのか。次回以降に続く。

nakano-tomofumi.hatenablog.com

mac の gimp から印刷

背景

証明書写真を印刷したい。1mm もサイズが違わないように印刷したい。

結論

macgimp から印刷はうまくいかない。一旦、jpeg にして、jpegmac の previewer で詳細な印刷設定をしてうまくいく。

印刷前までの方法

ほぼ、下記の通り

fanblogs.jp

ただし事前に次のようなプロセスがあった。

背景にできる影

壁までの距離が近いと、本人の影ができる。 だから、壁までの距離がそれなり(3m)にある場所を選んだ。

縦撮り

横にしても横が無駄に広いばかりか、上と下が規定のサイズに収まらないことが過去にあった。証明写真は縦向きなんだから縦撮り。

フラッシュ

フラッシュは使ったが、手で遮って、反射光を天井に当てるようにした。ビルトインのフラッシュじゃなければ特にこのあたりのテクニックは必要なさそう。

切り抜き

証明写真を加工する前に、始めに証明写真のサイズに切り抜きたい。なぜなら、切り抜きの段階で写真が不適切なことがわかることもあるから。 自分はpicasa を利用しているので picasa で切り抜きを証明写真のサイズのカスタムアスペクト比を先に作ってから、目視 で切り抜き。

gimp での加工

背景の処理

背景はオフホワイトとかでもNGで完全な白が必要らしい。そこで背景を選択し、白にした。

レンジの調整

これは

上記のページのように、写真を入れる。

既にサイズ通りの比になっているので特に難しいことは無いはず。

背景に grid を書く。

背景が白だと、どこを切ったらいいかわからない。トンボを入れると、ぎりだと写真の中に線が入ってしまう。 無難そうな水色の格子を背景に作った。

https://docs.gimp.org/ja/plug-in-grid.html

gimp での印刷

何度やってもうまくいかない。そもそも後部トレーの紙を給紙しなくてはいけないのに、普通紙を給紙してくる。馬鹿か。 そして、結論の方法を試し、やっと印刷できた。