ためすう
Metrics optimization
学習目標
- Describe the role of correct metric optimization method in a competition
- Analyze new metrics
- Create constant baselines
- Recall the most important classification and regression metrics
- Describe what libraries can be used to optimize a particular metric
Regression metrics review II
今回対象のメトリクス
- ®MSPE, MAPE
- ®MSLE
MSPE: Mean Square Percentage Error
$$ MSPE = \frac{100\%}{N}\sum_{i=1}^N(\frac{y_i - \hat{y_i}}{y_i})^2 $$
最適な定数は加重平均
MAPE: Mean Absolute Percentage Error
$$ MSPE = \frac{100\%}{N}\sum_{i=1}^N|\frac{y_i - \hat{y_i}}{y_i}| $$
最適な定数は加重中央値
®MSLE: Root Mean Square Logarithmic Error
対数スケールで計算された RMSE
$$ RMSLE = \sqrt{\frac{1}{N}\sum_{i=1}^N (log({y_i}+1) - log(\hat{y_i} + 1))^2} $$
$$ = RMSE(log({y_i}+1), log(\hat{y_i} + 1) = \sqrt{MSE(log({y_i}+1), log(\hat{y_i} + 1)} $$
対数空間で RMSE の定数を見つけ、対数空間から逆変換する必要がある
参考
第5章 変更に追従する: Observer part1
2019-06-22「Rubyによるデザインパターン」の読書メモです。
時々、個人の見解入りです。
用語
Subject: サブジェクト
ニュースを持っているクラス
Observer: オブザーバ
ニュースを得ることに関心のあるオブジェクト
変更前
class Payroll
def update(changed_employee)
puts("#{changed_employee.name}のために小切手を切ります!")
puts("彼の給料はいま#{changed_employee.salary}です!")
end
end
class Employee
attr_reader :name, :title, :salary
def initialize(name, title, salary, payroll)
@name = name
@title = title
@salary = salary
@payroll = payroll
end
def salary=(new_salary)
@salary = new_salary
@payroll.update(self)
end
end
payroll = Payroll.new
fred = Employee.new('Fred', 'Crane Operator', 3000, payroll)
fred.salary = 3500
継承を利用したパターン
class Subject
def initialize
@observers = []
end
def add_observer(observer)
@observers << observer
end
def delete_observer(observer)
@observers.delete(observer)
end
def notify_observers
@observers.each do |observer|
observer.update(self)
end
end
end
class Payroll
def update(changed_employee)
puts("#{changed_employee.name}のために小切手を切ります!")
puts("彼の給料はいま#{changed_employee.salary}です!")
end
end
class Employee < Subject
attr_reader :name, :title, :salary
def initialize(name, title, salary)
super()
@name = name
@title = title
@salary = salary
end
def salary=(new_salary)
@salary = new_salary
notify_observers
end
end
fred = Employee.new('Fred', 'Crane Operator', 3000)
payroll = Payroll.new
fred.add_observer(payroll)
fred.salary = 3500
モジュールを利用したパターン
module Subject
def initialize
@observers = []
end
def add_observer(observer)
@observers << observer
end
def delete_observer(observer)
@observers.delete(observer)
end
def notify_observers
@observers.each do |observer|
observer.update(self)
end
end
end
class Payroll
def update(changed_employee)
puts("#{changed_employee.name}のために小切手を切ります!")
puts("彼の給料はいま#{changed_employee.salary}です!")
end
end
class Employee
include Subject
attr_reader :name, :title, :salary
def initialize(name, title, salary)
super()
@name = name
@title = title
@salary = salary
end
def salary=(new_salary)
@salary = new_salary
notify_observers
end
end
fred = Employee.new('Fred', 'Crane Operator', 3000)
payroll = Payroll.new
fred.add_observer(payroll)
fred.salary = 3500
ライブラリを利用したパターン
require 'observer'
class Payroll
def update(changed_employee)
puts("#{changed_employee.name}のために小切手を切ります!")
puts("彼の給料はいま#{changed_employee.salary}です!")
end
end
class Employee
include Observable
attr_reader :name, :title, :salary
def initialize(name, title, salary)
@name = name
@title = title
@salary = salary
end
def salary=(new_salary)
@salary = new_salary
changed
notify_observers(self)
end
end
fred = Employee.new('Fred', 'Crane Operator', 3000)
payroll = Payroll.new
fred.add_observer(payroll)
fred.salary = 3500
Ruby で MessagePack を使ってみる
2019-06-21やったこと
MessagePack を使ってみます。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ gem list | grep msg
msgpack (1.2.10)
調査
gem インストール(もし必要だったら)
Gemfile
gem 'msgpack'
$ bundle install
pack と unpack してみる
MessagePack is an efficient binary serialization format
圧縮効率が良いらしいです。
$ rails c
Running via Spring preloader in process 56887
Loading development environment (Rails 5.2.3)
irb(main):001:0> msg = [1, 2, 3, "漢字"].to_msgpack
=> "\x94\x01\x02\x03\xA6\xE6\xBC\xA2\xE5\xAD\x97"
irb(main):002:0> MessagePack.unpack(msg)
=> [1, 2, 3, "漢字"]
メモリのサイズを見てみる
$ rails c
Running via Spring preloader in process 57354
Loading development environment (Rails 5.2.3)
irb(main):001:0> v = [1, 2, 3, "漢字"]
=> [1, 2, 3, "漢字"]
irb(main):002:0> require 'objspace'
=> true
irb(main):003:0> p ObjectSpace.memsize_of(v)
72
=> 72
irb(main):004:0> msg = [1, 2, 3, "漢字"].to_msgpack
=> "\x94\x01\x02\x03\xA6\xE6\xBC\xA2\xE5\xAD\x97"
irb(main):005:0> p ObjectSpace.memsize_of(msg)
40
=> 40
オブジェクトは圧縮されているようです。 (memsize_of で返される値は完全ではないらしい)
参考
第4章 アルゴリズムを交換する: Strategy part2
2019-06-20「Rubyによるデザインパターン」の読書メモです。
時々、個人の見解入りです。
クラスベースの Strategy ではなく、コードブロックベースの Strategy パターンで書いてみます。
Strategy を適用する (簡易版)
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(&formatter)
@title = '月次報告'
@text = ['順調', '最高']
@formatter = formatter
end
def output_report
@formatter.call(self)
end
end
HTML_FORMATTER = lambda do |context|
puts '<html>'
puts "<head><title>#{context.title}</title></head>"
puts('<body>')
context.text.each do |line|
puts("<p>#{line}</p>")
end
puts('</body>')
puts '</html>'
end
PLAIN_TEXT_FORMATTER = lambda do |context|
puts("*****#{context.title}*****")
context.text.each do |line|
puts(line)
end
end
report = Report.new &HTML_FORMATTER
report.output_report
report = Report.new &PLAIN_TEXT_FORMATTER
report.output_report
コードブロックのストラテジは、そのインターフェースが単純で、1つのメソッドで事足りるようなときにのみ有効に働きます。
sidekiq で ログ出力する
2019-06-20やったこと
Rails5 で利用している sidekiq のログを出力します。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ rails --version
Rails 5.2.3
$ gem list | grep sidekiq
sidekiq (5.2.7)
前提
sidekiq がインストール済みとします。
調査
ログ出力の設定
config/initializers/sidekiq.rb
Sidekiq::Logging.logger.level = Logger::DEBUG
sidekiq を再起動
$ bundle exec sidekiq -C config/sidekiq.yml
ログを確認
queue を登録して、worker を実行すると下記のログが流れます。
$ less log/sidekiq.log
2019-06-15T14:17:59.230Z 61557 TID-ovrdi8s0t TaskWorker JID-fe2e63bbd5eb7d0221a72f55 INFO: start
2019-06-15T14:17:59.233Z 61557 TID-ovrdi8s0t TaskWorker JID-fe2e63bbd5eb7d0221a72f55 INFO: id 出力
2019-06-15T14:17:59.233Z 61557 TID-ovrdi8s0t TaskWorker JID-fe2e63bbd5eb7d0221a72f55 INFO: 333
2019-06-15T14:17:59.233Z 61557 TID-ovrdi8s0t TaskWorker JID-fe2e63bbd5eb7d0221a72f55 INFO: done: 0.003 sec
参考
第4章 アルゴリズムを交換する: Strategy part1
2019-06-19「Rubyによるデザインパターン」の読書メモです。
時々、個人の見解入りです。
継承に基づいた設計は、いくつかの深刻な不利益をもたらします。たとえどんなに注意深くコードを設計しても、サブクラスはそのスーパークラスに依存してしまいます。
そこで、「委譲」を使ってコードを書き直してみます。
Strategy を適用する (version1)
class Formatter
def output_report(title, text)
raise 'Abstract method called'
end
end
class HTMLFormatter < Formatter
def output_report(title, text)
puts '<html>'
puts "<head><title>#{@title}</title></head>"
puts('<body>')
text.each do |line|
puts("<p>#{line}</p>")
end
puts('</body>')
puts '</html>'
end
end
class PlainTextFormatter < Formatter
def output_report(title, text)
puts("*****#{title}*****")
text.each do |line|
puts(line)
end
end
end
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(formatter)
@title = '月次報告'
@text = ['順調', '最高']
@formatter = formatter
end
def output_report
@formatter.output_report(@title, @text)
end
end
report = Report.new(HTMLFormatter.new)
report.output_report
report.formatter = PlainTextFormatter.new
report.output_report
formatter を外から注入しているので、依存性の注入と言えそうです。
コンテキストとストラテジの間でのデータを共有する方法
- コンテキストがストラテジオブジェクトのメソッドを呼び出すときに引数で渡す
- ストラテジへと渡されたコンテキスト自身の参照を使う
- データの流れが単純化されますが、コンテキストとストラテジ間の結合度を上げてしまうことにもなります。
Strategy を適用する (version2)
Formatter クラスはインターフェースを規定するためにしか、存在しません。
Ruby のダックタイピングの哲学に反するため、削除します。
class HTMLFormatter
def output_report(title, text)
puts '<html>'
puts "<head><title>#{@title}</title></head>"
puts('<body>')
text.each do |line|
puts("<p>#{line}</p>")
end
puts('</body>')
puts '</html>'
end
end
class PlainTextFormatter
def output_report(title, text)
puts("*****#{title}*****")
text.each do |line|
puts(line)
end
end
end
class Report
attr_reader :title, :text
attr_accessor :formatter
def initialize(formatter)
@title = '月次報告'
@text = ['順調', '最高']
@formatter = formatter
end
def output_report
@formatter.output_report(@title, @text)
end
end
report = Report.new(HTMLFormatter.new)
report.output_report
report.formatter = PlainTextFormatter.new
report.output_report
sidekiq で namespace を使えるようにする
2019-06-19やったこと
Rails5 で sidekiq を利用しているのですが、queue に namespace を指定するとエラーが発生しました。
今回はこのエラーを解消を目指します。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ rails --version
Rails 5.2.3
$ gem list | grep sidekiq
sidekiq (5.2.7)
前提
sidekiq がインストール済みとします。
調査
発生したエラー
config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.redis = { url: 'redis://localhost:6379', namespace: 'sidekiq' }
end
namespace を指定しました。
エラー
2019-06-15T07:14:53.315Z 47524 TID-ov7xxfh88 INFO: Booting Sidekiq 5.2.7 with redis options {:url=>"redis://localhost:6379", :namespace=>"sidekiq", :id=>"Sidekiq-server-PID-47524"}
2019-06-15T07:14:53.426Z 47524 TID-ov7xxfh88 INFO: Running in ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
2019-06-15T07:14:53.426Z 47524 TID-ov7xxfh88 INFO: See LICENSE and the LGPL-3.0 for licensing details.
2019-06-15T07:14:53.426Z 47524 TID-ov7xxfh88 INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org
2019-06-15T07:14:53.429Z 47524 TID-ov7xxfh88 ERROR: Your Redis configuration uses the namespace 'sidekiq' but the redis-namespace gem is not included in the Gemfile.Add the gem to your Gemfile to continue using a namespace. Otherwise, remove the namespace parameter.
どうやら、redis-namespace をインストールする必要があるようです。
redis-namespace をインストール
Gemfile
gem 'redis-namespace'
$ bundle install
設定ファイルの修正
キューを入れるときの設定も追記します。
※ namespace も myapp に変えました。
config/initializers/sidekiq.rb
Sidekiq.configure_server do |config|
config.redis = { url: 'redis://localhost:6379', namespace: 'myapp' }
end
Sidekiq.configure_client do |config|
config.redis = { url: 'redis://localhost:6379', namespace: 'myapp' }
end
rails と redis をそれぞれ再起動したら、OKです。
参考
第3章 アルゴリズムを変更する: Template Method
2019-06-18「Rubyによるデザインパターン」の読書メモです。
時々、個人の見解入りです。
修正前
例えば下記のようなコードがあったとします。
ここから、htmlではなくプレーンテキスト形式でもレポートを表示する要件が加わったとき、どうすれば良いでしょうか。
class Report
def initialize
@title = '月次報告'
@text = ['順調', '最高']
end
# 簡略化
def output_report
puts('<html>')
puts("<title>#{@title}</title>")
@text.each do |line|
puts("<p>#{line}</p>")
end
puts('</html>')
end
end
Report.new.output_report
修正後
class Report
def initialize
@title = '月次報告'
@text = ['順調', '最高']
end
def output_report
output_start
output_head
output_body_start
output_body
output_body_end
output_end
end
private
def output_body
@text.each do |line|
output_line(line)
end
end
def output_start
end
def output_head
output_line(@title)
puts
end
def output_body_start
end
def output_line(line)
raise "Called abstract method: #{__method__}"
end
def output_body_end
end
def output_end
end
end
class HTMLReport < Report
private
def output_start
puts '<html>'
end
def output_head
puts "<head><title>#{@title}</title></head>"
end
def output_body_start
puts('<body>')
end
def output_line(line)
puts("<p>#{line}</p>")
end
def output_body_end
puts('</body>')
end
def output_end
puts '</html>'
end
end
class PlainTextReport < Report
private
def output_line(line)
puts(line)
end
end
p '-- HTMLReport --'
HTMLReport.new.output_report
p '-- PlainTextReport --'
PlainTextReport.new.output_report
これで新しいフォーマットが必要になった場合も、サブクラス追加で出来ますね。
※ 本のサンプルコードに少し手を加えています。
おまけ
ダックタイピング
「もしアヒルのように見えて、アヒルのように鳴くのなら、それはアヒルである」
注意点
すべての起こりうる事態に対処するために、サブクラスに大量のメソッドをオーバーライドさせるようなテンプレートクラスを作成するのは避けましょう。
sidekiq で管理画面を表示する
2019-06-18やったこと
sidekiq で管理画面を表示できるようにします。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ rails --version
Rails 5.2.3
$ gem list | grep sidekiq
sidekiq (5.2.7)
調査
config/routes.rb
require 'sidekiq/web'
mount Sidekiq::Web, at: '/sidekiq'
http://localhost:3000/sidekiq/
にアクセスすると管理画面が見られます。
参考
序章,第1章 よいプログラムとパターン
2019-06-17「Rubyによるデザインパターン」の読書メモです
時々、個人の見解入りです。
序章
デザインパターンは、ソフトウェアを構築する際に繰り返し発生する一般的な問題を認識し、解決する手助けをするという意味があります。
第1章 よいプログラムとパターン
パターンのためのパターン
変わるものを変わらないものから分離する
理想的なシステムでは、すべての変更は局所的です。すべてのコードをくまなく調べる必要がないようにすべきです。
コードの流れを追うという意味では、そうだと思います。
同じプロダクトだけど、管理しているソースコードが違ったりすると、これは難しくなりそうです。
インターフェースに対してプログラムし、実装に対して行わない
この記事に似たようなことを書いた気がするので置いておきます。
継承より集約
継承
継承は、まさにその性質から、サブクラスをスーパークラスに結びつける傾向があります。スーパークラスの振る舞いを変更すれば、それはサブクラスの振る舞いを変えてしまうことになります。
test1.rb
class Vehicle
def start_engine
p "#{self.class.name}:#{__method__}"
end
def stop_engine
p "#{self.class.name}:#{__method__}"
end
end
class Car < Vehicle
def sunday_drive
start_engine
# ... something
stop_engine
end
end
Car.new.sunday_drive
集約
test2.rb
class Engine
def start
p "#{self.class.name}:#{__method__}"
end
def stop
p "#{self.class.name}:#{__method__}"
end
end
class Car
def initialize
@engine = Engine.new
end
def sunday_drive
@engine.start
# something...
@engine.stop
end
end
Car.new.sunday_drive
Engine クラスを切り出しました。
Engine の差し替えもやりやすくなります。
委譲、委譲、委譲
委譲先のオブジェクトに責任転嫁する際に、追加のメソッド呼び出しが必要になります。
=> Ruby ではそのようなメソッドは書かなくてもOK (詳しくは、第10章、第11章)
必要になるまで作るな
YAGNI 原則
「You Ain’t Gonna Need It (必要になるまで作るな)」
将来のことは誰にも分からないので、いつか必要になる可能性もあると同時に
必要にならない可能性もあります。
本書で扱うGoFの14パターン
- Template Method
- Strategy
- Observer
- Composite
- Iterator
- Command
- Adapter
- Proxy
- Decorator
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Interpreter
さらに
- 内部ドメイン特化言語
- 専門分野に特化した小さな言語を作るとても動的な仕組み
- メタプログラミング
- 必要なクラスやオブジェクトを実行時に動的に作るテクニック
- Convention over Configuration (CoC)
- 構成ファイル (たいていはXML)の憂鬱への治療薬