pandas.groupbyでaggに自作関数を渡す、要素にnumpy.arrayを使う。

import pandas as pd
import numpy as np


def g(x):
    y = None
    for i, a in x.iteritems():
        if y is None:
            y = a.copy()
        else:
            y += a
    return Foo(y)


class Foo:
    def __init__(self, x):
        self.x = x

    def __str__(self):
        return str(self.x)


if __name__ == '__main__':
    df = pd.DataFrame({'a': [1, 1, 2], 'b': [np.array([1, 2]), np.array([1, 2]), np.array([1, 2])]})
    x = df.groupby('a').agg({'b': g})
    print(repr(x))
    print(repr(df))

sklearn.ensemble.AdaBoostClassifierの使い方

公式ドキュメント

パラメータ

  • base_estimator=None,
  • n_estimators=50,
  • learning_rate=1.0,
  • algorithm=‘SAMME.R’,
  • random_state=None

GridSearchを使ったbase estimatorのパラメータ調整

  • 下記のようにkeyの前にbase_estimator__をつけたら良い。
param_grid = {'base_estimator__max_depth': [4, 5, 6, None], 'base_estimator__max_features': [2, None],
              'base_estimator__min_samples_split': [2, 8, 16, 32],
              'base_estimator__min_samples_leaf': [2, 8, 16, 32], 'base_estimator__max_leaf_nodes': [50, None],
              'learning_rate': [0.5, 1, 4]}

sklearn.svm.SVCの使い方

公式ドキュメント

パラメータ

  • C=1.0
  • kernel=‘rbf’
  • degree=3
  • gamma=‘auto’
  • coef0=0.0
  • shrinking=True(調査中)
  • probability=False
  • tol=0.001
  • cache_size=200(調査中)
  • class_weight=None
  • verbose=FalseC
  • max_iter=-1
  • decision_function_shape=None(調査中)
  • random_state=None

パラメータを変えて様子をみる。

サンプルデータ

f:id:nsb248:20170224232501p:plain

デフォルトのまま

  • accuracy: 0.635
  • std: 0.362 f:id:nsb248:20170224221555p:plain

C

  • Cの意味を表した式はこちらを参照
  • Cは学習データに対する分類の正しさとモデルの複雑さのトレードオフをコントロールする。
  • Cが大きいと分類の正しさを優先し、モデルが複雑になる(モデルパラメータのノルム-2が大きくなる)。
  • 分類(C=10, kernel=‘rbf’) f:id:nsb248:20170224232758p:plain
  • 分類(C=1000) f:id:nsb248:20170224232817p:plain

kernel

  • 取りうる値:

    • ‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’ or a callable
  • 分類(kernel=‘linear’) f:id:nsb248:20170224233545p:plain

  • 分類(kernel=‘poly’) f:id:nsb248:20170224233604p:plain

  • 分類(kernel=‘rbf’) f:id:nsb248:20170224233616p:plain

  • 分類(kernel=‘sigmoid’) f:id:nsb248:20170224233636p:plain

  • rbf以外上手く分類できなかった。原因を調査中。

degree

  • kernel=‘poly'のときのみ使用できる。
  • polynomialの次数を指定する。

gamma

  • kernel=‘rbf’, ‘poly’, ‘sigmoid’のとき使用できる。
  • gammaの意味を表した式はこちらを参照
  • 分類(kernel=‘rbf’, gamma=0.1) f:id:nsb248:20170224235706p:plain
  • 分類(kernel=‘rbf’, gamma=2.0) f:id:nsb248:20170224235739p:plain

coef0

  • kernelのindependent term。kernel=‘poly’, ‘sigmoid'のとき重要。

RandomForestClassifierの使い方

公式ドキュメント

パラメータ

  • DecisionTreeのアンサンブル学習なので多くはDecisionTreeと同じ。こちらを参照

    特有のパラメータ

  • n_estimators
  • bootstrap
  • oob_score
  • n_jobs
  • verbose
  • warm_start(調査中)

パラメータを変えて様子をみる。

n_estimators

  • 他のパラメータはデフォルトのまま。つまり、汎化能力は低いdecision treeの組み合わになる。
  • AUCの推移 f:id:nsb248:20170224185532p:plain
  • estimatorの数が1のときに比べ、数個のestimatorを加えるだけでかなり精度が上がっている。

bootstrap

  • ツリー構築時に学習データからbootstrapをするかのフラグ。bootstrapをすることで性能が向上する。相関が減るからだっけ?(確認中)
  • AUCの推移
    • 赤がTrue、青がFalse f:id:nsb248:20170224192550p:plain

verbose

  • 途中のツリー構築処理のログを出力してくれる。
  (prop.get_family(), self.defaultFamily[fontext]))
[Parallel(n_jobs=1)]: Done  10 out of  10 | elapsed:    0.0s finished
[Parallel(n_jobs=1)]: Done  10 out of  10 | elapsed:    0.0s finished
[Parallel(n_jobs=1)]: Done  10 out of  10 | elapsed:    0.0s finished

DecisionTreeClassifierの限界

これまで

限界を探る。

関係のないデータを混ぜる。

  • x2というデータを入れる。
  • targetはx2に全く依存していない。

    結果

  • accuracy: 0.810
  • std: 0.100
  • 分岐ツリー f:id:nsb248:20170224174722p:plain
  • 重要度
{'x2': 0.24604190914667379, 'x1': 0.45724873019357137, 'x0': 0.29670936065975489}
  • かなり精度が悪化した。
  • 関係ないはずのx2の重要度が0.25もある。

データを45度回転させる。

結果

  • accuracy: 0.835
  • std: 0.037
  • 分類 f:id:nsb248:20170224180953p:plain
  • 分岐ツリー f:id:nsb248:20170224181001p:plain
  • 多少ではあるが精度が悪化している。
  • 分類結果を見ると境界が直線であってほしいにも関わらず、階段状になってしまう。理論上仕方ないが。

DecisionTreeClassifierのパラメータ調整

設定

  • DecisionTreeClassifierの使い方はこちら
  • 下記のデータの分類をDecisionTreeClassifierを使って行う
    • 約10%ほどノイズが入っている。
    • x0: (0.5, 1.5), x1:(0.5, 1.5)で分岐すれば精度90%で分類するごとができる。 f:id:nsb248:20170224165542p:plain
  • 何も制約を入れずに全データを使って学習させると、下記の通りオーバーフィッティングしている。

とりあえずパラメータを変えてみる

  • クロスバリデーションを使ってモデルの評価を行う。

    デフォルトパラメータの場合(ツリーへの制約がない状態)

  • accuracy: 0.800
  • std: 0.039
  • 分類 f:id:nsb248:20170224165616p:plain
  • 分岐ツリー f:id:nsb248:20170224165639p:plain
  • かなり複雑になっている。
  • 90%くらいの精度になってほしいが、80%くらい。

max_depthを調整する。

max_depth=3

  • accuracy: 0.777
  • std: 0.035
  • 分類 f:id:nsb248:20170224170115p:plain
  • 分岐ツリー f:id:nsb248:20170224170128p:plain

max_depth=4

  • accuracy: 0.889
  • std: 0.038
  • 分類 f:id:nsb248:20170224170242p:plain
  • 分岐ツリー f:id:nsb248:20170224170248p:plain

  • max_depth=4でかなりいい感じの結果になった。

GridSearchを使って最適なパラメータを探す。

  • accuracy: 0.882
  • std: 0.044
  • 分類 f:id:nsb248:20170224171535p:plain
  • 分岐ツリー f:id:nsb248:20170224171543p:plain

まとめ

  • ツリーの大きさを制約するようなパラメータを上手く設定することで、汎化性能が上がることを確認。

DecisionTreeClassifierの使い方

公式ドキュメント

パラメータを変えて様子をみる。

サンプルデータ

decision treeで分類しやすいように格子状のデータを作成する。 f:id:nsb248:20170224141608p:plain

パラメータを変えて実験

デフォルトのパラメータのまま

  • 分類結果 f:id:nsb248:20170224141857p:plain:w200
  • 分岐ツリー f:id:nsb248:20170224141902p:plain:w200
  • 重要度
{'x1': 0.69674185463659166, 'x0': 0.30325814536340839}
  • ちゃんとすべて分類できている。線形で分類できるからな。
  • feature importance で本当はx0とx1の重要度は同じはず、ただ、最初の分岐がx0だったので、x0の方が高くなったいる。使用する乱数によって変わる。

criterion=‘entropy’

  • 分類結果 f:id:nsb248:20170224144138p:plain:w200
  • 分岐ツリー f:id:nsb248:20170224144147p:plain:w200
  • 今回のケースだと何もかわらず。。
  • どう結果に影響するかを他のケースを作って検証しよう(また今度)

splitter=‘random’

  • 分類結果 f:id:nsb248:20170224144549p:plain:w200
  • 分岐ツリー f:id:nsb248:20170224144600p:plain:w200
  • 重要度
{'x1': 0.51479236812570128, 'x0': 0.48520763187429866}
  • 境界をランダムに決めているので、分岐ツリーが巨大になっている。
  • 重要度は近い値に。

max_features=1

  • 今、特徴数は2なので、1とした場合をみる。2の場合は良い方で分岐するが、1だと乱数次第。
  • 分類結果 f:id:nsb248:20170224145125p:plain:w200
  • 分岐ツリー f:id:nsb248:20170224145138p:plain:w200
  • 重要度
{'x0': 0.052631578947368363, 'x1': 0.94736842105263164}
  • 複雑な分岐になる。
  • 重要度も乱数次第なので、当てにならない。

max_depth=3

  • ツリーの深さの最大を3とする。3に達したらそこで終了
  • 分類結果 f:id:nsb248:20170224150236p:plain:w200
  • 分岐ツリー f:id:nsb248:20170224150239p:plain:w200

min_samples_split=80

  • nodeに残っているサンプル数が80個未満になったら分岐せずにそこで終了。
  • 分類結果 f:id:nsb248:20170224150033p:plain:w200
  • 分岐ツリー f:id:nsb248:20170224150035p:plain:w200

min_samples_leaf=11

  • リーフに残るサンプル数の最低数を指定
  • 分類結果 f:id:nsb248:20170224151055p:plain:w200
  • 分岐ツリー f:id:nsb248:20170224151058p:plain:w200

min_weight_fraction_leaf = 6 / 400

  • class_weightを指定していないので、min_samples_leaf=6と同じ。
  • 分類結果 f:id:nsb248:20170224151323p:plain:w200
  • 分岐ツリー f:id:nsb248:20170224151325p:plain:w200

max_leaf_nodes=10

  • リーフの最大個数を指定。今回は10個のリーフができている。
  • 分類結果 f:id:nsb248:20170224151457p:plain:w200
  • 分岐ツリー f:id:nsb248:20170224151501p:plain:w200

min_impurity_split=0.48

  • criterionが指定した値以下になったら、そこで分岐終了
  • 分類結果 f:id:nsb248:20170224151817p:plain:w200
  • 分岐ツリー f:id:nsb248:20170224151819p:plain:w200

まとめ

  • 今回は各パラメータの意味を確認できた。
  • キレイに分類できるようにデータを作っている&予測をしていないので、当然、パラメータ調整しても意味がない。

ソースコードの一部

import numpy as np
import pandas as pd
from sklearn import tree
import nsb
import pydotplus


def grid_data():
    n = 20
    x0, x1 = np.meshgrid(np.linspace(0, 2, n), np.linspace(0, 2, n))

    def is_even(x):
        return np.round(x) % 2 == 0

    y = np.logical_xor(is_even(x0), np.logical_not(is_even(x1))).astype(np.int64)
    return pd.DataFrame({'x0': x0.ravel(), 'x1': x1.ravel(), 'y': y.ravel()})


if __name__ == '__main__':
    df = grid_data()
    clf = tree.DecisionTreeClassifier(min_impurity_split=0.48)
    clf.fit(df[['x0', 'x1']], df['y'])
    nsb.plot.scatter_with_boundary(df['x0'], df['x1'], df['y'], clf, 'fig.png')
    dot_data = tree.export_graphviz(clf, out_file=None, feature_names=['x0', 'x1'])
    graph = pydotplus.graph_from_dot_data(dot_data)
    graph.write_png('tree.png')

    print(dict(zip(['x0', 'x1'], clf.feature_importances_)))