PythonでUpcast/Downcast出来ない件と多態性のサンプル
継承・カプセル化・多態性 オブジェクト指向の必須要素はこれ。継承、カプセル化・多態性。 異種リストを書けば全部試せるので試してみる。 多態性実現のためにオブジェクトのUpcast/Downcastを調べようとしたら存在しない!。 結論として、PythonにおいてはオブジェクトのUpcast/Downcastは出来ない。 動的型付けなんで、Upcastは勝手に上位クラスだと思って触れば良いのだけど、 形だけでも上位クラスにキャストしておいた方が可読性は上がると思うので残念。 abstractメソッドを定義するだけでパッケージを要求するとか切ない。 from abc import ABCMeta, abstractmethod # 抽象基底クラス class Base: __attr = 100 @abstractmethod def hoge(self): pass class Derived1(Base): __attr = 200 def hoge(self): print(self.__attr) class Derived2(Base): __attr = 300 def hoge(self): print(self.__attr) vals = [Derived1(), Derived2()] for val in vals: val.hoge() なお、Downcastについては、以下のようにしている例がある。 class SubDerived2(Derived1): __attr = 500 def hoge(self): print(self.__attr) def myfunc(self): print(\"myfunc is called\") derived = Derived2() derived.__class__ = SubDerived2 derived.myfunc() 確かに、Upcastは動的型付けの範囲内だが、実現可能でDowncastは何らかの指示が必要で、 必要な方にだけ手段が用意されているのは合理的な気もする。 関数 defで定義する。最初の行に書いた文字列リテラルはdocstring。PHPDoc的なやつだろうか。 関数のスコープからローカル->グローバル->ビルトインの順にシンボルを参照できる。 関数スコープから外のスコープのシンボルは書けない。(global指定して初めて書ける)。 フィボナッチ数列。 def fib(n): \"\"\"Print a Fibonacci series up to n.\"\"\" a,b = 0,1 while a < n: print(a,end=' ') a,b=b,a+b print() fib(100) # 0 1 1 2 3 5 8 13 21 34 55 89 キーワード引数も使える。 def myfunc(hoge,fuga=1): print(hoge) print(fuga)
Laravel Accessor/Mutatorを使って透過的にフィールドを暗号化/復号するサンプル
DBに入っているデータを決まった書式/形式に変換して表示したり、 逆に逆変換して保存する例は多いかと思います。 変換,逆変換の実装方法は以下みたいな感じかと..。 いずれも変換/逆変換の存在を忘れて仕様が抜けたり、 同じことを他でも書くコードクローンが発生する原因になる。 Controllerにダラダラと変換/逆変換を書く EloquentにオレオレSetter/Getterを書く Accessor/Mutatorを使うことで上記の原因を無くすことができます。 Accessor/Mutator Eloquentのメンバ変数(つまり、テーブルのフィールド)へのアクセスを ある規則をもってEloquentに定義したSetter/Getterを仲介するように強制できます。 [clink implicit=\"false\" url=\"https://laravel.com/docs/5.8/eloquent-mutators\" imgurl=\"http://laravel.jp/assets/img/logo-head.png\" title=\"Eloquent: Mutators Introduction\" excerpt=\"Accessors and mutators allow you to format Eloquent attribute values when you retrieve or set them on model instances. For example, you may want to use the Laravel encrypter to encrypt a value while it is stored in the database, and then automatically decrypt the attribute when you access it on an Eloquent model.\"] Accessors and mutators allow you to format Eloquent attribute values when you retrieve or set them on model instances. For example, you may want to use the Laravel encrypter to encrypt a value while it is stored in the database, and then automatically decrypt the attribute when you access it on an Eloquent model. In addition to custom accessors and mutators, Eloquent can also automatically cast date fields to Carbon instances or even cast text fields to JSON. 暗号化/復号 サンプル 標題の通りですが、Accessor/Mutatorを使ってフィールドを暗号化/復号してみます。 Cryptファサードを使ってAES-256-CBCの暗号化/復号を行う対です。 secretvalueというフィールドにAES256CBCで暗号化して書き込み、復号して読み込みます。 class User extends Authenticatable { use Notifiable; /** * Get the user\'s secretvalue. * * @param string $value * @return string */ public function getSecretvalueAttribute($value) { return decrypt($value); } /** * Set the user\'s secretvalue. * * @param string $value * @return string */ public function setSecretvalueAttribute($value) { $this->attributes[\'secretvalue\'] = encrypt($value); $this->save(); } } 透過的に呼び出す例です。 Userのsecretvalueフィールドに\"hogehoge\"という値を設定しています。 hogehogeという平文を暗号化してsecretvalueフィールドに書き込む処理は使う側には見えません。 Route::get(\'/sample/setvalue\',function(){ AppUser::find(1)->secretvalue = \'hogehoge\'; }); Userのsecretvalueフィールドを読み込んで出力しています。 暗号化済み文字列を復号する処理は使う側には見えません。 Route::get(\'/sample/getvalue\',function(){ echo AppUser::find(1)->secretvalue; }); より広い用途で使える 暗号化/復号はかなり直球な使い方ですが、ビジネスロジック内の定型処理など 積極的に使おうとするとAccessor/Mutatorに掃き出せるケースがありそうです。
インデントとブロック,比較と、比較の連接,辞書オブジェクト,リストとタプル,if … elif … else,for,while,iteratableオブジェクト
他言語からの流入組が最小工数でPythonを学ぶシリーズです。 Python文法詳解を読みながらアウトプットしていきます。 [arst_toc tag=\"h4\"] いくら何でも公式を上から順番に読むのは時間がかかり過ぎるし、 550ページとかある本なんて読みたくないので、なるべく薄くて枠だけ書いてある本が良いなと。 Python文法詳解posted with amazlet at 19.05.25石本 敦夫 オライリージャパン 売り上げランキング: 432,601Amazon.co.jpで詳細を見る インデントとブロック ブロックはbeginや { などの予約語ではなく、インデントを使う。 公式は以下。 [clink implicit=\"false\" url=\"https://docs.python.org/2.0/ref/indentation.html\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"2.1.7 Indentation\" excerpt=\"Leading whitespace (spaces and tabs) at the beginning of a logical line is used to compute the indentation level of the line, which in turn is used to determine the grouping of statements.\"] 公式に面白い例が載ってた。 見た目上、インデントが無茶苦茶だけれども文法的には合理性がある例。 要するにインデントとインデント解除のconsistencyがあればOK。 def perm(l): # Compute the list of all permutations of l if len(l) <= 1: return [l] r = [] for i in range(len(l)): s = l[:i] + l[i+1:] p = perm(s) for x in p: r.append(l[i:i+1] + x) return r コーデイング規約によると、お作法としてスペースの個数は4個。だそうだ。 タブではなくスペースでインデントするなんて信じられない...。 エディタにタブ=スペース4個の設定を追加しないと。 言語仕様上の制約はインデントのconsistencyだけなので、 実際のインデント数はプロジェクトによるとのこと。ほうほう。 ただ、スペースとタブの混在禁止はこの際ハッキリさせましょう、という流れ。 比較と、比較の連接 比較演算子について公式は以下。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/library/stdtypes.html#comparisons\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"2.1.7 Indentation\" title=\"比較\" excerpt=\"Python には 8 種の比較演算があります。比較演算の優先順位は全て同じです (ブール演算より高い優先順位です)。比較は任意に連鎖できます; \"] 比較演算子を連接できる。PHPとRubyには無い感じ。 a=50 # 通常 if (0 < a) and (a < 100): print("OK") else: print("NG") # 連接 if 0 < a < 100: print("OK") else: print("NG") 辞書オブジェクト KeyValue型。本家は以下。Keyは変更不能で一意でなければならない、と書いてある。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/tutorial/datastructures.html#dictionaries\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"5.5. 辞書型 (dictionary)\" excerpt=\"もう一つ、有用な型が Python に組み込まれています。それは 辞書 (dictionary)です。辞書は他の言語にも 連想記憶 (associated memory) や 連想配列 (associative array) という名前で存在することがあります。ある範囲の数でインデクス化されているシーケンスと異なり、辞書は キー (key) でインデクス化されています。\"] a = {1:\'one\',2:\'two\'} print(a) # {1:\'one\',2:\'two\'} # 代入 print(a[1]) # \'one\' a[1] = \'壱\' print(a[1]) # \'壱\' # 削除 del a[1] print(a[1]) # エラー キーを文字列にして連想配列みたくできる。 連想配列のキーにできるかどうかは、その型が更新可能であるかどうか? b = {\'hoge1\':\'fuga1\',\'hoge2\':\'fuga2\'} print(b) # {\'hoge1\':\'fuga1\',\'hoge2\':\'fuga2\'} リストとタプル 似て非なる配列的データ構造、リストとタプル。 リストの方は更新可能。タプルは更新不能。 任意の型を要素としてとれるので、配列ではなくリスト。 L1 = [1,2,3,4,5] print(L1) # [1,2,3,4,5] L2 = (1,2,3,4,5) print(L2) # (1,2,3,4,5) L1[0] = \'hoge\' print(L1) # [\'hoge\', 2, 3, 4, 5] L2[0] = 100 # TypeError: \'tuple\' object does not support item assignment 序数を使ってシーケンシャルアクセスできる。 ary = [1,5,3,4,9] print(ary[3]) # 4 str = \'abcdef\' print(str[1]) # b 負の序数を使うと後ろから数える。 ary = [1,5,3,4,9] print(ary[-1]) # 4 レンジも使える。 ary = [1,5,3,4,9] print(ary[1:3]) # [5,3,4] print(ary[-3:-1]) # [5,3,4] print(ary[:3]) # [1,5,3] レンジ指定で要素を更新できる。 ary = [1,5,3,4,9] ary[2:3] = [100,200] print(ary)# [1, 5, 100, 200, 4, 9] if ... elif ... else elseif が elif。 論理式が偽になるのは\"数値の0\"、空のコレクション、Noneオブジェクト。それ以外は真。 a = 50 if 0 < a < 35: print("OK") elif 35 <= a < 100: print("So much") else: print("NG") for,while forループとwhileループがある。iteratableオブジェクトを指定できる。 whileループについて。 str = \"aiueo\" stop = len(str) i=0 while i < stop: print(str[i]) i += 1 # a # i # u # e # o forループについて。 str = \'abcde\' for c in str: print(c) # a # b # c # d # e # continue, break str = \'abcde\' for c in str: if c == \'c\': continue if c == \'e\': break print(c) # a # b # d whileループ、forループの終了時に実行するブロックをelseで定義できる。 breakで中断した場合は実行されない。 str = \'abcde\' for c in str: print(c) else: print(\'ループ終了\') # a # b # c # d # e # ループ終了 iteratableオブジェクト よくあるパターン。要素の順序を走査するためのインターフェースを実装したオブジェクト。 本家によるとこんな感じ。 [clink implicit=\"false\" url=\"https://docs.python.org/ja/3/glossary.html#term-iterable\" imgurl=\"https://www.python.org/static/img/python-logo@2x.png\" title=\"iterable\" excerpt=\"(反復可能オブジェクト) 要素を一度に 1 つずつ返せるオブジェクトです。 反復可能オブジェクトの例には、(list, str, tuple といった) 全てのシーケンス型や、 dict や ファイルオブジェクト といった幾つかの非シーケンス型、 あるいは Sequence 意味論を実装した __iter__() メソッドか __getitem__() メソッドを持つ任意のクラスのインスタンスが含まれます。\"]
回帰直線の当てはまりの指標
[mathjax] 前の記事で線形単回帰において訓練データから回帰係数を求める方法を書いてみた。 標本平均を使って母平均を推測する話とリンクさせることで、 回帰係数の95%信頼区間を求めることができた。 回帰係数(hat{beta_0},hat{beta_1})と真の回帰係数(beta_0,beta_1)の関係がこれ。 [clink url=\"https://ikuty.com/2019/05/15/linear_regression_evaluate/\"] RSE,真の回帰直線と観測データがどれくらい離れているか 真の回帰直線がわかったとしても、全てのデータが回帰直線の上に乗っているのでなければ、 回帰直線を使って値を予測したときに誤差が出てくる。 残差平方和(Residual sum of square)。WikipediaにもRSS。 (hat{y_i})は訓練データを使って得られた回帰係数で作った回帰直線で予測した値。 だから、RSS自体も訓練データに対応して変動する。 begin{eqnarray} RSS=sum_{i=1}^n (y_i-hat{y_i})^2 end{eqnarray} で、知りたいのはRSSが訓練データに対してどの程度変動するかだから標準偏差。 標本分散は不偏推定量ではなくて分布の自由度で割る必要がある...という話があって、 不偏推定量を求める段取りが必要。(n-1)ではなく(n-2)で割る!。詳しくは以下。 カイ2乗分布になりそうだけれども、自由度が何故(n-2)なのだろうか...。 begin{eqnarray} RSE= sqrt{frac{1}{n-2}sum_{i=1}^{n}(y_i-hat{y_i}^2)} end{eqnarray} [clink implicit=\"false\" url=\"https://stats.stackexchange.com/questions/204238/why-divide-rss-by-n-2-to-get-rse/377869\" imgurl=\"https://cdn.sstatic.net/Sites/stats/img/logo.svg?v=60d6be2c448d\" title=\"Why divide RSS by n-2 to get RSE?\" excerpt=\"The reason is based on trying to get an unbiased estimator of the underlying error variance in the regression. In a simple linear regression with normal error terms it can be shown that:That is, under the standard assumption of normally distributed errors, the residual sum-of-squares has a chi-squared distribution with ?−2 degrees of freedom. \"] 決定係数(R^2) RSS,RSEは(Y)の単位で値が決まる。(y_i)が無茶苦茶大きいとRSEは大きくなる。 RSEだけ見て回帰直線がどれだけ当てはまっているか言えない様子。 当てはまりの良さを(0)から(1)の範囲におさめる別の指標もある。 TSS (Total sum of square)として以下。 begin{eqnarray} TSS = sum_{i-1}^{n}(y_i-bar{y_i})^2 end{eqnarray} (R^2)として以下。 begin{eqnarray} R^2 &=& frac{TSS-RSS}{TSS} \\ &=& 1-frac{RSS}{TSS} \\ &=& 1 - frac{sum_{i=1}^n (y_i-hat{y_i})^2}{sum_{i-1}^{n}(y_i-bar{y_i})^2} end{eqnarray}
Model Binding と 1枚のBladeで CRUD する
1枚のBladeで確認画面付きCRUDを実現できると、Bladeの枚数が格段に少なくなって良さそう。 その前にまずModelBindingで単なるUserを1枚のBladeでCRUDしてみる。 1枚のBladeが複数の機能で使われることになり、Bladeの中に要素と制御が増えていくため、 実は、Bladeの枚数が増えたとしても1つのBladeを単純にした方が良いのかもしれないが、 1度作っておくとずっと使えるかもしれないので、そこまでやってみる。 やること Laravelに最初から付いてくるUserを使って、name,email,passwordのCRUDをする。 URL(route)は以下。showのパラメタをOptionalにして、あればUpdate、なければCreateする。 Update、Createは、本質的に分けるべきと考えてURLを別にしてある。 firstOrNew()を使うと、あればUserインスタンスを読み込んでくれる。 なければインスタンスを作る。ただしレコードは作らない。新規作成操作時にレコードを作成する。 <?php /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the \"web\" middleware group. Now create something great! | */ Route::get(\'/user/{user?}\',\'UserController@show\'); Route::post(\'/user/\',\'UserController@add\')->name(\'postAddUser\'); Route::post(\'/user/{user}\',\'UserController@edit\')->name(\'postEditUser\'); コントローラ コントローラは以下。無条件に保存するだけなのでほとんど何も書いてない。 条件が増えてくるとそれなりに行数が増える。 ModelBindingの良さは、タイプヒンティングでEloquentのインスタンスを受けられること。 変数を受けてEloquentインスタンスを探す手間がバッサリ無い。 RequestValidatorは載せていません。 <?php namespace AppHttpControllers; use AppHttpRequestsAddUserRequest; use AppHttpRequestsEditUserRequest; use AppUser; class UserController extends Controller { public function show($id=null) { $user = User::firstOrNew([\'id\'=>$id]); return view(\'user\',compact(\'user\')); } public function add(User $user,AddUserRequest $request) { $user->fill($request->only([\'name\',\'email\',\'password\']))->save(); return view(\'user\',compact(\'user\')); } public function edit(User $user,EditUserRequest $request) { $user->fill($request->only([\'name\',\'email\',\'password\']))->save(); return view(\'user\',compact(\'user\')); } } Blade 肝心のBladeは以下。これだけなのに結構書かないといけない。 laravelcollective/htmlは大分前にLaravelから外れていて、使わない方が良いのかも。 自力でHTMLを書くのと大して労力が変わらない可能性がある。 結構書かないといけないから1枚にしたいのか、複数枚でよければあまり書かなくて良いのか、 微妙なところ。Laravel5.7なのでBootstrap4。validation用のクラスが全然違う。 親Blade(layouts.app)は何でも良いので載せていません。 @extends(\'layouts.app\') @section(\'content\') @if (isset($user->id))編集 @else 追加 @endif @if ($user->wasRecentlyCreated) {!! Form::model($user,[\'route\'=>[\'postEditUser\',$user->id],\'class\'=>\'form-horizontal\'])!!} @else {!! Form::model($user,[\'route\'=>[\'postAddUser\'],\'class\'=>\'form-horizontal\'])!!} @endif {!! Form::label(\'name\', \'名前 :\') !!} @if($errors->has(\'name\')) {!! Form::text(\'name\',$user->name,[\'class\'=>\'form-control is-invalid\']) !!} @else {!! Form::text(\'name\',$user->name,[\'class\'=>\'form-control\']) !!} @endif {!! $errors->first(\'name\') !!} {!! Form::label(\'email\', \'email :\') !!} @if($errors->has(\'email\')) {!! Form::email(\'email\',$user->email,[\'class\'=>\'form-control is-invalid\']) !!} @else {!! Form::email(\'email\',$user->email,[\'class\'=>\'form-control\']) !!} @endif {!! $errors->first(\'email\') !!} {!! Form::label(\'password\', \'password :\') !!} @if($errors->has(\'password\')) {!! Form::password(\'password\',[\'class\'=>\'form-control is-invalid\']) !!} @else {!! Form::password(\'password\',[\'class\'=>\'form-control\']) !!} @endif {!! $errors->first(\'password\') !!} @if($user->wasRecentlyCreated) {!! Form::submit(\'保存\',[\'class\'=>\'btn btn-primary form-control col-sm-2\']) !!} @else {!! Form::submit(\'新規作成\',[\'class\'=>\'btn btn-primary form-control col-sm-2\']) !!} @endif {!! Form::close() !!} @endsection まとめ relationもないし懸案の確認画面もないので、単純。 次回、has a、has many relation版と、確認画面付きの版を試します。
単回帰曲線における回帰係数の精度(95%信頼区間)
[mathjax] 線形単回帰で推定する回帰係数の精度を評価する方法を読んだのでまとめてみる。 当然、真の直線はわからないのだけれども、真の直線があると仮定した上で 推定した回帰係数との関係を考えることで、回帰係数の精度について話せるようになる。 回帰係数の導出 データポイントが(n)個ある状況。 ( (x_1,y_1),(x_2,y_2),cdots,(x_n,y_n) ) 回帰係数(hat{beta_0})と(hat{beta_1})を使って線形回帰したい。 begin{eqnarray} hat{y} = hat{beta_0} + hat{beta_1} x end{eqnarray} データポイントと回帰直線の差を残差平方和(RSS,redisual sum of square)で表す。 データポイントは既に与えられているデータなので、(hat{beta_0},hat{beta_1})の関数。 begin{eqnarray} f(hat{beta_0},hat{beta_1}) = (y_1 -hat{beta_0}-hat{beta_1}x_1)^2 + (y_2 - hat{beta_0}-hat{beta_1}x_2)^2 + cdots + (y_n - hat{beta_0}-hat{beta_1}x_n)^2 end{eqnarray} RSSを最小にする(hat{beta_0})と(hat{beta_1})を求めるために、(hat{beta_0})、(hat{beta_1})それぞれで偏微分して(0)として解く。 なんでそれぞれ個別に偏微分して0と置いて良いかは、 RPML読もうとして力尽きたときに理解したので省略。 参考にした本に( hat{beta_0}),(hat{beta_1}),RSSの3次元の図があって、確かにそれで良さそうな予感。 begin{eqnarray} frac{partial}{partial hat{beta_0}} f(hat{beta_0},hat{beta_1}) = 0 \\ frac{partial}{partial hat{beta_1}} f(hat{beta_0},hat{beta_1}) = 0 \\ end{eqnarray} 以下のようになるらしい。(bar{x})、(bar{y})はデータポイントの標本平均。 なので、データポイントがわかれば計算で求まる。 begin{eqnarray} hat{beta_1} &=& frac{sum_{i=1}^n (x_i-bar{x}) (y_i-bar{y}) }{sum_{i=1}^n (x_i-bar{x})^2 }\\ hat{beta_0} &=& bar{y}-hat{beta_1}bar{x} end{eqnarray} 母回帰直線の推定 データポイントが同じであれば(hat{beta_0}),(hat{beta_1})は同じになるけれども、 データポイントを取り直して異なるデータセットにすると、(hat{beta_0}),(hat{beta_1})は微妙に違う値になる。 じゃあ、データセットを大量に用意したとして、(hat{beta_0}),(hat{beta_1})を計算しまくると、 どこかに収束するんじゃなかろうか。 標本が大量にあると標本平均は母平均に収束する。標準偏差はより小さくなる。 つまりデータが大量にあると、母平均からのズレが小さくなっていく。 大数の弱法則、中心極限定理、ルートnの法則。 begin{eqnarray} hat{sigma} &=& frac{sigma}{sqrt{n}} \\ hat{sigma}^2 &=& frac{sigma^2}{n} end{eqnarray} begin{eqnarray} lim_{n rightarrow infty} hat{sigma}^2 = lim_{n rightarrow infty} frac{sigma^2}{n} = 0 end{eqnarray} [clink url=\"https://ikuty.com/2018/07/17/sample_sigma/\"] (hat{beta_0}),(hat{beta_1})は母回帰直線からどれくらいばらついているのか。 (hat{beta_0}),(hat{beta_1})の分散は以下を使うらしい。 両方に出てくる(sigma^2)は、母回帰直線と回帰直線の差となる項の散らばり度合い。 つまり、(Y=beta_0 + beta_1 X + epsilon )としたときの(epsilon)の分散。 begin{eqnarray} sigma_{hat{beta_0}}^2 &=& sigma^2 Bigl[frac{1}{n} + frac{bar{x}^2}{sum_{i=1}^n (x_i-bar{x})^2} Bigr] \\ sigma_{hat{beta_1}}^2 &=& frac{sigma^2}{sum_{i=1}^n (x_i -bar{x})^2} end{eqnarray} (x_i)が散らばれば散らばるほど、(sigma_{hat{beta_1}}^2)は小さくなる。 データポイントの(x)成分が小さい方から大きい方まで含まれれば、傾き(beta_1)を推定しやすくなる。 そして、(bar{x}=0)であるならば、(hat{beta_0})の散らばりは、(hat{mu})の散らばりと等しくなる。 最終的に求めたいのは不明な(sigma^2)だが、(sigma^2)はデータから計算できる。 (sigma)の推定値(RSE,Resual Standard Error)はRSSから推定する。 begin{eqnarray} sqrt{frac{f(hat{beta_0},hat{beta_1})}{(n-2)}} end{eqnarray} (hat{beta_1})の標準偏差がわかったので、95%信頼区間を求めることができる。 線形回帰における(hat{beta_1})の95%信頼区間は、 begin{eqnarray} Bigl[ hat{beta_1} - 1.96 sigma_{hat{beta_1}},hat{beta_1} + 1.96 sigma_{hat{beta_1}} Bigr] end{eqnarray} 同様に(hat{beta_0})の95%信頼区間は、 begin{eqnarray} Bigl[ hat{beta_0} - 1.96 sigma_{hat{beta_0}},hat{beta_0} + 1.96 sigma_{hat{beta_0}} Bigr] end{eqnarray}
稼働中のEC2のコピーを作成してALB下で切り替えた話 WordPress Update Blue Green
稼働中のEC2を落とさないでALB下で切り替えた作業記録を書いてみます。 こちら↓の方が詳しく書いてあります..。今回書いた記事の特徴は以下となります。 AutoScalingグループを使わない ALBの下で切り替える deploy手順をAWSの外に用意する [clink implicit=\"false\" url=\"https://qiita.com/keitakn/items/6abe6c971e4dec3b69ef\" imgurl=\"https://camo.qiitausercontent.com/08bed869c98443e0474ce8ce78bdbe964a09f1e9/68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f37313839392f62303437313164612d623035362d313262662d646164612d3439653930333231663366342e6a706567\" title=\"AWS CodeDeploy でEC2のBlue/Greenデプロイを作成する\" excerpt=\"AWS CodeDeploy を使ってBlue/Green デプロイの仕組みを構築する為の手順を紹介します。Blue/Greenデプロイとは?現在稼働している環境と別にもう1つ稼働環境を作成し、ロードバランサー等のルーティングを新環境に向けるデプロイ方法です。常にリクエストを受けている稼働中のサーバを置き換えるよりも安全にデプロイ可能なのがメリットになります。\"] [arst_adsense slotnumber=\"1\"] 現状と動機 WPCoreアプデとセキュリティパッチはマメに当てないといけないと痛感して、 仕方なくBlue Green的な方法を導入してみた。 ALBの下にWebサーバ1台。Webサーバの下にDB用EC2が1台(非RDS)。 アップデート対象はWebサーバのみ。 Webサーバ内でWordPressが12個、SNIで動いてる。 x.smallクラス。CloudWatchを見るとLoadAverageは常時30%くらい。 全てのサイトで、pluginでファイルとDBをS3にバックアップしている。 localで開発/WPCore,plugin update/動作確認後、止めずにansibleでdeploy。 deploy実行前に\"メンテナンス中\"に設定。deploy完了後に解除する。 アップデート中は管理画面操作禁止を通達。 deploy、パッチ当てでコケると、S3から戻すまで止まる! S3から戻らないと終わる。 やったこと deploy、アップデート時にのみ、WebサーバのEC2をコピーする。 ALBのターゲットグループにコピーしたEC2を追加する。 元のEC2に対してansibleでdeployする。 元のEC2に対してパッチアップデートする。 ALBの先を元のEC2に戻す。 コピーしたEC2を削除する。 メディアライブラリにファイルをアップロードすると差分が発生するため、 元EC2と一時EC2のファイル達を同期させないといけないけれど、 メンテ中は、管理画面操作を禁止できるという状況であることと、 もともとEC2が1台なのでその仕組みを作っていないから、それは2台以上に増えたときに..。 AMI作成 まずAMIを作成。AMIとはAmazon Machine Imageの頭文字。 [clink implicit=\"false\" url=\"https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instances-and-amis.html\" imgurl=\"https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/images/architecture_ami_instance.png\" title=\"インスタンスと AMI\" excerpt=\"Amazon マシンイメージ (AMI) は、ソフトウェア構成 (オペレーティングシステム、アプリケーションサーバー、アプリケーションなど) を記録したテンプレートです。AMI から、クラウドで仮想サーバーとして実行される AMI のコピーであるインスタンスを起動します。以下の図に示すように、1 つの AMI の複数のインスタンスを起動することができます。\"] 手順は以下。 ダッシュボードからコピーしたいインスタンスを選択 アクション->イメージ->イメージの作成を選択 デフォルトだと、AMI作成時にコピー元インスタンスが自動的に停止し、コピー後に自動的に再起動する。 \"再起動しない\"にチェックをいれることで、コピー元インスタンスの停止/再起動を抑止できる。 \"再起動しない\"にチェックをいれないでAMIを作成すると、コピー元が止まってしまうので注意! イメージの作成を押下すると、作成処理が始まる。 EBSが30GiBだと、完了まで1時間程度要してしまった。 ダッシュボード -> AMI から AMIの作成状況を確認できる。 ステータスが available になれば完了。 インスタンスの起動 作成済みAMIからインスタンスを起動する。 ダッシュボード -> AMI を開く 起動したいAMIを選択する アクション -> 起動を押下 すると、インスタンスタイプを聞かれる。 進捗状況はEC2ダッシュボードで確認できる。 ALBのターゲットグループ変更 既にALBのターゲットグループに元EC2が属していて、 セキュリティグループが正しく設定済みで、ヘルスチェックが通っている前提。 現状、ALBの下は元EC2だけなので、AvailabilityZoneは1種類だけ。 ダッシュボード -> ターゲットグループ そこに、新しく作成したインスタンスを追加する。 新しいインスタンスのセキュリティグループを旧インスタンスに合わせて ALBからのInboundを受けられるようにすること。 新しいインスタンスのヘルスチェックが無事通って2台構成になった図。 元のEC2をターゲットグループから削除 元のEC2をターゲットグループから削除する。 EC2ダッシュボードのモニタリングタブをみて、CPU使用率などに変動があることを確認する。 元のEC2に対してゴニョゴニョする ALBのターゲットグループから外れた元のEC2に対してdeployなりパッチ当てを実行する。 元のEC2にElasticIPを当てておけば、再起動してもIPアドレスは変わらない。 この手順においては、新規作成したEC2にElasticIPを当てる必要はない。 元のEC2のセキュリティグループを0.0.0.0/0:80からアクセスできるようにして、 hostsにElasticIPを書いてアクセスするなどの方法で、元のEC2にアクセスする。 このためだけにALBを作って置いておくとそのALBに対して課金されてしまう。 新規作成したEC2を無料枠でやったりしてスペックが低いとパフォーマンスが下がる。 非稼働のEC2に当てたElasticIPで課金される。 一時EC2をターゲットグループから外し、元のEC2を投入する 上の手順を逆から実施。つまり、一時EC2をターゲットグループから外し、元のEC2を投入する。 [arst_adsense slotnumber=\"1\"]
損失関数の評価,バイアス-バリアンスと過学習のトレードオフ
[mathjax] 損失関数をバイアス項、バリアンス項、削減不能誤差の和に分解できることと、 損失は削減不能誤差より下回らないこと、バイアス項、バアリアンス項のトレードオフが起こること、 を読んだ。過学習っていうのはこういうことなのか、と腑に落ちたので記念に記事を書いてみる。 (式変形は細かいところで間違ってるのと、おっさんのチラシの裏なので参考にしないでください) 2乗損失の期待値の式変形 モデルを作った後、訓練データ、テストデータそれぞれの全データについて、 2乗損失の期待値(MSE)を求め、モデルの当てはまりの良さを調べるらしい。 2乗損失を以下のように式変形する。条件付き期待値(E(t|x))ってなんだ...。 begin{eqnarray} L(y(x),t)^2 &=& (y(x)-t)^2 \\ &=& (y(x)-E(t|x)+E(t|x)-t)^2 \\ &=& left( left( y(x)-E(t|x)right) + left( E(t|x) - y(x)right) right)^2 \\ &=& (y(x)-E(t|x))^2 + 2(y(x)-E(t|x))(E(t|x)-t) + (E(t|x)-t)^2 \\ end{eqnarray} 2乗損失の期待値(MSE)は以下。 第2項は(x)、(t)で積分するとゼロになる! begin{eqnarray} E[L(y(x),t)^2] &=& E[ (y(x)-E(t|x))^2 + 2(y(x)-E(t|x))(E(t|x)-t) + (E(t|x)-t)^2 ] \\ &=& E[ (y(x)-E(t|x))^2 + (E(t|x)-t)^2 ] end{eqnarray} 和の期待値は期待値の和なので、 begin{eqnarray} E[L(y(x),t)^2] = E[ (y(x)-E(t|x))^2 ] + E[(E(t|x)-t)^2 ] end{eqnarray} (x)の出処がテストデータではなく訓練データですよ、と明示するために、 以下みたいな書き方に改める。この式の中で(y(x;D))が学習で得られるモデル。 第2項は学習とは関係なく発生する数値。 begin{eqnarray} E_D[L(y(x;D),t)^2] &=& E_D[ (y(x;D)-E(t|x;D))^2 ] + \\ && E_D[(E(t|x;D)-t)^2 ]] end{eqnarray} 第1項の式変形を続ける。括弧が多すぎて力尽きた..。 余計な項を足して引いて次の式変形の足しにするタイプ。 begin{eqnarray} E_D[ (y(x;D)-E(t|x;D))^2 ] &=& E_D[ ( { y(x;D)-E_D[(y(x;D))] } ] &+& { E_D[ y(x;D)] - E[t|x;D])^2 } \\ &=& E_D [ { (y(x;D))-E_D[ y(x;D)] }^2 ] + \\ &=& E_D [ { E_D[ y(x;D)-E[t|x;D] ] }^2 ] end{eqnarray} バイアス・バリアンスと削減不能誤差 以下はバリアンス項と書かれている。 モデル((y(x;D))による予測が訓練データ集合によって変動する度合いの期待値。 異なる訓練データを使ったときにどの程度モデルが変化するかを表す。 過学習の度合い。 begin{eqnarray} E_D bigl[ bigl{ (y(x;D))-E_D[ y(x;D)] bigr}^2 bigr] end{eqnarray} 以下はバイアス項と書かれている。 複雑な事象を単純なモデルで近似したことによる誤差、と書かれてる。 例えば、3次関数+ノイズから発生するデータを直線で近似すると、モデルが単純すぎて値が大きくなる。 モデルが複雑になればなるほどバイアス項は減っていく様子。 未学習の度合い。 begin{eqnarray} E_D bigl[ bigl{ E_D[ y(x;D)]-E[t|x;D] bigr}^2 bigr] end{eqnarray} で、一番最初に出てきたモデルと関係ない以下。 バイアス、バリアンス共に非負の値だから、2乗損失の期待値は以下より小さくなることはない。 奇跡的にバイアス、バリアンス共にゼロだったとしても、以下は学習とは関係なく発生する。 削減できない誤差。 begin{eqnarray} E_Dbigl[bigl(E(t|x;D)-tbigr)^2 ]bigr] end{eqnarray} 結局よくわからない...。体感の結論.. 訓練データを使ってモデルを複雑にしていけばいくほど、 モデルが訓練データにフィットするようになるが、 その訓練データにフィットしまくったモデルは、未知のテストデータを予測しづらくなる。 モデルの複雑度が\"ある程度\"のところまでは、バリアンスの上昇よりもバイアスの低下が効くから、 訓練データに対する2乗誤差、テストデータに対する2乗誤差ともに減少する。 モデルの複雑度が\"ある程度\"を超えると、バイアスの低下が頭打ちになる一方でバリアンスが上昇し、 訓練データに対する2乗誤差が低下する一方で、テストデータに対する2乗誤差が上昇する。 どう頑張っても、削減不可能な誤差が存在する。 条件付き期待値(E(t|x))の意味を理解できずプロットすることは叶わなかった。
損失関数
[mathjax] おっさんが入門した軌跡シリーズです。損失関数に関して学んだことをメモしておきます。 入力値(x)、正解(t)からなる訓練データ(T=(x,t))が大量に与えられたときに、 (f(x,w))によって回帰なり分類なりをする。 仮に立てた(f(x,w))と正解(t)の距離(Lleft(f(x,w),tright))を損失関数と呼んだり。 あえて(Lleft(f(x,w),tright))としているのは、 一番わかりやすそうな残差の2乗だけでなく、他があるから。 2乗損失 残差の2乗だと、(f(x,w))と(t)の差が大きい場合に必要以上に大きくなってしまう。 ほとんどのデータで残差が0なのに、特殊なデータで残差が100とかになられたら、 全体の損失は測れそうにないし、異常値に敏感すぎる。 begin{eqnarray} Lleft(f(x,w),tright) = (t-f(x,w))^2 end{eqnarray} Huber損失 単に残差の2乗を使うだけでは不十分で、\"(f(x,w))と(t)の差の大小にあまり影響されないこと\"が必要。 残差の絶対値がある値を超えるまでは残差の2乗、 超えてからは線形にするというのもある(Huber損失)。 begin{eqnarray} Lleft(f(x,w),tright) = begin{cases} (t-f(x,w))^2 & t in [t-delta,t+delta] \\ 2delta (|t-f|-frac{delta}{2}) & それ以外 end{cases} end{eqnarray} 損失関数が微分可能か 損失関数を最小(極小)にすることが目的なので..、 損失関数の1階導関数(勾配ベクトル)を使ってパラメタを更新したりする。 begin{eqnarray} w_{i+1} = w_i - eta left(f\'(x,w),tright) end{eqnarray} 損失関数が微分可能(連続)だと勾配ベクトルがすぐに求まるので、 損失関数は不連続点をなくすように作るらしい。 (Huber損失の2乗から線形に切り替わるところは連続になってる。) 次回以降実際のデータとモデルを使ってやってみる。
決定木の分割基準,情報ゲイン,エントロピー
[mathjax] 集合に対して再帰的に境界を入れていく操作が決定木の作成。 では、集合のどこに境界を入れれば良いか。 属性をテストすることにより得られる情報量が最も大きくなるように入れる。 汎化能力、みたいな言葉を読んでいくにあたってこの先、結構抽象的な話になるので一度確認。 データが(n)個のクラスに分類できるとして、クラス(i)に属する確率を(P_i)とする。 このとき、あるデータがクラス(i)に属することを知るには(log_2 frac{1}{P_i})の情報量が必要。 その期待値は(I(P_1,P_2,cdots,P_n)=sum_{i=1}^{n} P_i log_2 frac{1}{P_i} = - sum_{n}^{i=1} P_i log_2 P_i)。 情報量の平均をエントロピーとか。 (D_p)個のデータを(D_{left})、(D_{right})に分割するとする。 その時、属性(f)に関する問いを使って分割する。 2分木の左ノード、右ノードだからleft,right。 (I(D_p))は分割前のエントロピー。 (I(D_{left}))、(I(D_{right}))は分割後のエントロピー。 分割前には(I(D_p))ビット必要だったのが、 分割後には(frac{N_{left}}{N_p} I(D_{left}) + frac{N_{right}}{N_p} I(D_{right}))ビット必要になった。と読むらしい。 その差を情報ゲイン(IG(D_p))と呼んで以下のように定義するらしい。 begin{eqnarray} IG(D_p,f) = I(D_P) - frac{N_{left}}{N_p} I(D_{left}) - frac{N_{right}}{N_p} I(D_{right}) end{eqnarray} 分割前よりも分割後の方が(IG(D_p,f))だけエントロピーが低い、という事実に関して、 分割に使った問いにより、(IG(D_p,f))の情報量を獲得した、と考えるらしい。 情報ゲインが最大になるような問いを根にもってきて、 再帰的に情報ゲインが大きいものから順に問うことで決定木を作っていく。