Normalizer

概要

sklearn.preprocessorsモジュールのNormalizerは、特徴量ベクトルのノルムが1になるようにする。具体的には、データごとに特徴量Fiを以下の式によってFi*に変換する。

(1)    \begin{equation*} {F_i}^* = \frac{\sum F_i}{\left( \sum {|F_i|}^p \right) ^\frac{1}{p}} \end{equation*}

ノルムのタイプはコンストラクターの引数で指定する。デフォルトは'l2'で、その他に'l1''max'を指定可能。

Normalizer(norm='l2')

挙動

それぞれ異なる正規分布に従う2つの特徴量について、Normalizerを適用したときの挙動を以下に示す。

scalerのような相似性の変換ではないので左下の変換後のヒストグラムは変換前の形状と異なっている。

データの空間的な分布は、デフォルトのL2ノルムの指定によって全データが半径1の円周上に位置するよう変換される。

変換後のデータを拡大してみると以下の通りで、原点を中心とした半径1の円周上に各点が並んでいる。

他の2つ、L1ノルムと最大値ノルムを指定して実行した結果が下記の通りで、それぞれのノルムに応じた線上に各点が並んでいる。

コードは以下の通りで、データに対してfit()メソッドでスケールパラメーターを決定し、transform()メソッドで変換を行うところを、これらを連続して実行するfit_transform()メソッドを使っている。

特徴

Normalizerは特徴量ベクトルの方向だけが重要な場合に用いる。たとえば空間内の特定の方向範囲にあるクラスターの分離などかと思うが、抽象的なものになると想像がつかない。実際、サイト上で見ても、Normalizerの意義とデータの性質に基づいて適用しているケースは、検索上位には出てこない。

なおNormalizerによる変換は不可逆であり、scalerのようなinverse_transform()を持たない。

 

イテレーターは再利用不可

イテレーターで生成されたオブジェクトを変数にセットして実行できるが、これをそのまま再度利用することはできない。

イテレーターはインスタンス生成時に__init__()メソッドにより初期化され、その後イテレーターとして使用が終わった直後の値を保持している。正確には、再利用が禁止されているのではなく初期状態から再度実行することができない、ということになる。

たとえば次の例を見ると、最初の実行が終わったのちに再度利用することは不可能ではない。ただし結果を見るとわかるように、2度目の実行の初期値が1度目の終了判定時の値4ではなく5から始まっている。

おそらくcountイテレーターの__next__()メソッドの最初で内部カウンターをインクリメントしていると考えられる。

このようにイテレーターの再利用は予想外の動作をすることがあるので控えた方がよさそうだ。

 

preprocessor – 異常値に対する頑健性

機械学習モデルにデータを適用するための前処理としていくつかのアルゴリズムによっては、異常値の影響を受けやすいことがある。

たとえば下図の左のような分布のデータがあるとする(平均が1、分散が1の正規分布に従う500個のランダムデータ)。そしてこのデータに値20の異常値が10個発生したとすると、全体の分布は右のようになる。

このデータに対して、MinMaxScalerStandardScalerRobustScalerで変換した結果を以下に示す。ただしStandardScalerRobustScalerについては、異常値は表示させず元の正規分布に係る範囲のみを表示している。

まず左側のMinMaxScalerについては、異常値を含めてレンジが0~1となるので、本体の正規分布のデータが0付近の小さな値に集中する。このため、本来学習の精度に効いてくるべき本体部分のデータの分離が十分でない可能性が出てくる。

真ん中のStandardScalerと右側のRobustScalerについては、本体部分の形は元の正規分布の形と大きく変わらず、頑健であることがわかる。

ここで異常値の個数を10個から20個に増やして、同じく3種類の変換を施してみる。

左側のMinMaxScalerについては、異常値の個数とは関係なくその値のみでレンジが決まり、元の分布が0付近に押し込められている状況は同じ。

真ん中のStandardScalerについては、10個の時に比べて少し分布の形が変わっていて、レンジが狭くなっている。

右側のRobustScalerについては、元の分布の形は大きくは変わっていない。

以上のことから、少なくとも3つの変換器について以下のような特徴があることがわかる。

  • MinMaxScalerは異常値によって本来分析したいデータのレンジが狭くなる可能性がある
  • StandardScalerは異常値の影響を受けにくいが、その大きさや頻度によって若干本体部分の分布が影響を受ける
  • RobustScalerは異常値の個数が極端に多くなければ、本来のデータの特性を頑健に保持する

なお、上記の作図のコードは以下の通り。

 

RobustScaler

概要

sklearn.preprocessingモジュールのRobustScalerは、各特徴量の中央値(medi)と第1-4分位数(q1i)、第3-4分位数(q3i)を用いて特徴量を標準化する。

(1)    \begin{equation*} {F_i}^* = \frac{F_i - med_i}{q_{3i} - q_{1i}} \end{equation*}

挙動

それぞれ異なる正規分布に従う2つの特徴量について、RobustScalerを適用したときの挙動を以下に示す。異なる大きさとレンジの特徴量が、変換後には原点を中心としてほぼ同じような広がりになっているのがわかる。

コードは以下の通りで、データに対してfit()メソッドでスケールパラメーターを決定し、transform()メソッドで変換を行うところを、これらを連続して実行するfit_transform()メソッドを使っている。

簡単なデータでRobustScalerの計算過程を確認しておく。以下の例では5個のデータにRobustScalerを適用している。これは1つの特徴量を持つ5個のデータを模していることになる。

インスタンス内に保持されたパラメーターのうち、center_は特徴量の標本平均、scale_が第3-4分位数-第1-4分位数となっていて、これらで各特徴量が標準化されているのが確認できる。

特徴

RobustScalerは異常値に対して頑健であり、StandardScalerより頑健性が高い。

 

StandardScaler

概要

sklearn.preprocessingモジュールのStandardScalerは、各特徴量の標本平均と標本分散を用いて特徴量を標準化する。

具体的には、特徴量Fiの標本平均(mi)と標本分散(vi)から以下の式により各特徴量FiFi*に変換する。

(1)    \begin{equation*} {F_i}^* = \frac{F_i -m_i}{\sqrt{v_i}} \end{equation*}

挙動

それぞれ異なる正規分布に従う2つの特徴量について、StandardScalerを適用したときの挙動を以下に示す。異なる大きさとレンジの特徴量が、変換後には原点を中心としてほぼ同じような広がりになっているのがわかる。

コードは以下の通りで、データに対してfit()メソッドでスケールパラメーターを決定し、transform()メソッドで変換を行うところを、これらを連続して実行するfit_transform()メソッドを使っている。

簡単なデータでStandardScalerの計算過程を確認しておく。以下の例では5個のデータにStandardScalerを適用している。これは1つの特徴量を持つ5個のデータを模していることになる。

インスタンス内に保持されたパラメーターのうち、mean_は特徴量の標本平均、var_は標本分散(不偏分散ではない)となっている。scale_はvar_の平方根。

各データの特徴量は次式で標準化されているのが計算で確認できる。

(2)    \begin{equation*} {F_i}^* = \frac{F_i - \rm{mean\_}}{\rm{scale\_}} = \frac{F_i - \rm{mean\_}}{\sqrt{\rm{var\_}}} \end{equation*}

特徴

StandardScalerは異常値の影響に対して比較的頑健である

 

MinMaxScaler

概要

sklearn.preprocessingモジュールのMinMaxScalerは、各特徴量が0~1の範囲に納まるように変換する。具体的には、特徴量Fiの最小値(mini)と最大値(maxi)から以下の式により各特徴量FiFi*に変換する。

(1)    \begin{equation*} {F_i}^* = \frac{F_i - min_i}{max_i - min_i} \end{equation*}

挙動

それぞれ異なる正規分布に従う2つの特徴量について、MinMaxScalerを適用したときの挙動を以下に示す。異なる大きさとレンジの特徴量が、変換後にはいずれも0~1の間に納まっているのが確認できる。

コードは以下の通りで、データに対してfit()メソッドでスケールパラメーターを決定し、transform()メソッドで変換を行うところを、これらを連続して実行するfit_transform()メソッドを使っている。

特徴

MinMaxScalerは簡明な方法だが、極端に値が離れた異常値が発生すると本来のデータがその影響を受ける場合がある。

matplot.pyplot – 格子でないグラフの組み合わせ

通常、Figure.subplots()pyplot.add_subplot()でグラフの描画領域を指定するとき、m行n列の格子状のグラフエリアが生成される

これに対して、たとえば1行目に2つのグラフエリアを表示して2行目に全幅のグラフを1つ、だとか、1列目に2列ぶち抜きのグラフエリアを表示して2列目に縦2つのグラフエリアを表示したいときがある。

このような場合の1つの方法が、Figure.add_subplotで加えたいグラフエリアの構成自体を変える方法がある。

以下の例は、1行目に2つのグラフを並べ、2行目は全幅で1つのグラフエリアを表示させる方法。

また、1列目に2行分を占有する一つのグラフエリアと、2列目に2つのグラフエリアを縦に並べる方法。

 

ndarrayの書式設定 – printoptions

概要

配列をprintで表示させようとして、書式設定でよく間違える。たとえば以下のように。

配列の各要素の書式を指定して表示させたい場合、formatメソッドではなく、Numpyのset_printoptionsを使う必要がある。

get_printoptions()

配列の書式オプションの一覧は、numpy.get_printoptions()で得られる。各オプションは辞書形式で保存されている。

set_printoptions()

これらのオプションを個別に設定するにはnumpy.set_printoptions()メソッドでキーと値を指定する。

numpy.set_printoptions([キー]=[値])

よく使いそうないくつかのオプションについてまとめる。

省略表示

thresholdedgeitems

要素数(列数・行数)がthresholdに指定した値を越えた場合に省略表示する。

edgeitemsは省略時に表示する要素数(列数・行数)を指定する。

threshold=0を指定すると、edgeitemsの値を超えると常に省略表示する(デフォルトの場合、edgeitems=3を越えると省略表示)。

2次元配列の行も同じ条件で省略表示される。

数値の書式

supress

デフォルトでは要素にオーダーが小さい数値が含まれていると浮動小数点表示となり、1つの要素でも浮動小数点表示になるとすべての要素が浮動小数点表示になる。

オプションで'supress=True'を指定すると、強制的に固定小数点で表示される。

precision

precisionで精度の桁数を指定する。固定小数点数の場合は小数点以下の桁数、浮動小数点数の場合は仮数部の桁数。

floatmode

floatmodeでキーワードを指定し、あらかじめ定められた書式を設定する。

次のような配列でキーワードごとの挙動を確認する。配列aは最大でもprecision設定より低い精度、配列bprecisionを超える精度の要素を持ち、デフォルトのprecision=8で表示が丸められている。

maxprec

デフォルトの設定。各要素がそれぞれ最大の精度で表示される。いずれの配列も、最大精度となる最後尾の要素の桁幅に統一されていて、0埋めはされない。デフォルトはこの設定なので、結果は上と同じ。

maxprec_equal

maxplecは0埋めされなかったが、maxprec_equalは最大精度の桁数に統一された上で0で埋められる(equalの意味が曖昧、maxprec_zeroとでもしてくれればよかったのに)。

fixed

全ての要素の精度がprecisionに統一され、それより低い精度の場合は0で埋められる。下の例では、2つの配列のすべての要素が小数点以下8桁に統一され、0で埋められている。

unique

precisionは無視され、各要素で必要な分だけの精度が保たれ、桁数は最大精度に統一される。配列bの最後の要素が丸められていないことに注意。

formatter

書式設定文字列とformatを渡して、任意の書式を設定する。渡し方は以下の通り。

formatter={'型名' : "{:書式}".format }

型名としては'int''float'のほか'numpystr'で文字列も指定できる。

 

Ruby – クラス

基本形

クラスの基本形は以下の通り。

  • クラス定義はclassで始めてendで終える
  • メソッドはdefで初めてendで終える
  • 初期化メソッド(コンストラクター)は'initialize()'
  • プロパティー(インスタンス変数)は頭に'@'をつけて、initialise()で定義
  • インスタンスの生成は[クラス名].new([引数])

initialize()~コンストラクター

インスタンス生成時の初期化処理をinitialize()に書く(コンストラクター)。インスタンス生成時にinitialize()が内部で実行され、その内容に沿った初期化が行われる(initialize()についてはこちら)。

メソッド

インスタンスメソッドはdef...endで定義する。引数を持たないメソッドの場合、()を省略してメソッド名だけで呼び出せる(これはnewについてもあてはまる)。

インスタンス変数へのアクセス

インスタンス変数はカプセル化(encapsulation、隠蔽)されている。参照したり値をセットしようとするとエラー。

インスタンス変数にアクセスするのにアクセスメソッドによる方法getter/setterを定義する方法がある。

クラス変数・クラスメソッド

クラス変数は、クラスから生成された全インスタンスが共通して利用する変数。

クラスメソッドはクラスレベルで定義されるメソッド。

クラスの継承

継承は'<'を使う。クラスの継承の詳細についてはこちら

 

Ruby – public/protected/private

概要

Rubyにおけるprivateprotectedは、C++やJavaでの振る舞いと一部で異なる。慣れている言語との違いというよりも、Rubyの場合言葉の概念から予想される挙動がシンプルに想起できない。

自クラス内での挙動

以下のコードで自クラス内での挙動を確認する。

直接呼出し

まず次のメソッドに着目する。

これらのメソッドを、インスタンスから直接呼び出した結果。publicメソッドは呼び出せるが、private/protectedメソッドは隠蔽されている。これは想定通り。

関数アクセス

次に、同一クラスの中でこれら3つのメソッドに関数としてアクセスするメソッドの挙動を見てみる。

これらのメソッドをインスタンスから呼び出した結果はすべてエラー無く、同一クラス内からは全て利用可能なことがわかる。

レシーバーアクセス

さらに、関数としてのアクセスではなく、レシーバーのメソッドとしてアクセスするメソッドを試してみる。

同じParentClassの新たなインスタンスを生成し、これを渡してみると、privateメソッドのみエラーとなる。

継承クラスでの挙動

ParentClassの継承クラスChildClassを定義し親クラスのメソッドを呼び出すと、上と同じ結果になり、privateprotectedは隠蔽されている。

関数呼び出しの場合、親クラスのメソッドに全てアクセス可能で、これも自クラス呼び出しと同じ。

別のChildClassのインスタンスを生成し、これをレシーバーとしてメソッドを呼び出すと、privateのみエラーとなる。

なお、関数呼び出し、レシーバー呼び出しの各internalメソッドをChildClass内でオーバーライドしても結果は同じになる。

まとめ

これらをまとめると次のようになる。

public protected private
自クラス
直接コール
× ×
自クラス
関数コール
自クラス
レシーバー
×
継承クラス
直接コール
× ×
継承クラス
関数コール
継承クラス
レシーバー
×

すなわち、public想定通り、関数コールの場合protectedprivateも自クラス・継承クラスに関わらず実行可能。レシーバー呼び出しの場合protectedは実行可能だがprivateは実行不可で、これも自クラス・継承クラスに関わらない。