分類の最近の記事

概要

Adversarial ValidationはTrainデータとTestデータの分布が異なる際に、Testデータに似たValidationデータを作成するのに使われる手法です。
Kaggleなどのデータ分析コンペではTrainデータとTestデータの一部が与えられ、コンペ終了まではこの一部のTestデータに対するスコアのみ知ることができます。一部のTestデータだけを見てモデルを評価していると、全体のテストデータに対しては良いスコアが出ずに最終的に低い順位に終わることがあります。ですのでCross Validationなどを用いて求めたValidationデータに対するのスコアと一部のTestデータに対するスコアに相関があることが望ましい状態です。そのために重要なことはTestデータに似た分布を持つValidationデータを作成することです。


手法

最終的な目標はTrainデータとTestデータを分類するモデルを作成することです。

まずどちらのデータセットからのデータかをラベル付けするために、TrainデータとTestデータにそれぞれ新しい目的変数を与えます。例えばTrainデータには "1"を、Testデータには"0"を与えます。そしてTrainデータとTestデータを結合して、データがどちら由来か予測するモデルを作成します。
もしTrainデータとTestデータが同じ分布を持つとするとどちら由来かを予測できないはずです。逆にTrainデータとTestデータの分布が異なる場合は予測は容易になります。
予測結果からTrainデータとTestデータの分布が異なるようであれば、Testデータの分布に似たValidationデータを作成する必要があります。これはTrainデータのうち、モデルがTestデータに由来する確率が高いと予測したデータを選ぶことで作成できます。


それでは必要なライブラリをインポートして実装に進みましょう。

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
%matplotlib inline
warnings.filterwarnings('ignore')
from tqdm import tqdm_notebook as tqdm

from sklearn.preprocessing import LabelEncoder
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
import lightgbm as lgb


TrainデータとTestデータの分布が同じ場合

まずデータセットを作成し、プロットしてみます。

X, y = make_classification(
    n_samples=10000,
    n_features=2,
    n_informative=1,
    n_redundant=0, 
    n_classes=2,
    n_clusters_per_class=1,
    class_sep=2.0,
    random_state=42
)

plt.scatter(X[y == 0, 0], X[y == 0, 1], s=10, alpha=0.5, label='class A')
plt.scatter(X[y == 1, 0], X[y == 1, 1], s=10, alpha=0.5, label='class B')
plt.legend(loc='upper right')
plt.show()

plot of dataset


このデータセットをTrainデータとTestデータに分け、Trainデータには "1"を、Testデータには"0"をそれぞれ新しく目的変数として与えます。その後、TrainデータとTestデータを結合して1つのデータセットに戻します。

X_train, X_test = train_test_split(X, test_size=0.33, random_state=42)

y_train = np.zeros(len(X_train))
y_test = np.ones(len(X_test))

X_all = np.concatenate([X_train, X_test], axis=0)
y_all = np.concatenate([y_train, y_test], axis=0)


これでTrainデータとTestデータが混ざったデータセットが出来上がりました。ここから新たに与えた目的変数を元にTrainデータとTestデータを分類するモデルを作成していきます。

X_train_adv, X_valid_adv, y_train_adv, y_valid_adv = \
    train_test_split(X_all, y_all, test_size=0.33, random_state=42, shuffle=True)

model = lgb.LGBMClassifier(
    n_estimators=1000,
    random_state=42)

model.fit(
    X_train_adv,
    y_train_adv,
    eval_set=[(X_train_adv, y_train_adv), (X_valid_adv, y_valid_adv)],
    eval_names=['train', 'valid'],
    eval_metric='auc',
    verbose=100)


このときのValidationデータを作成しモデルの性能を評価します。 ではモデルのパフォーマンスを見てみましょう。

ax = lgb.plot_metric(model.evals_result_, metric='auc')
plt.show()

plot of learing curve


結果を見ると、Validationデータに対してAUCはほぼ0.5あたりを示しています。AUCが0.5を示すということはモデルのパフォーマンスがランダムに分類するのと変わらないことを意味しています。これはモデルがTrainデータとTestデータをうまく分類できてないということです。つまりTrainデータとTestデータの分布が同じであることがわかります。


TrainデータとTestデータにの分布が異なる場合

次にTrainデータとTestデータの分布が異なる場合を見ていきましょう。

データはKaggleの2019 Data Science Bowlのデータを引っ張ってきました。データの中身や特徴量作成に関して、ここではコードだけにして割愛させていただきます。

test = pd.read_csv("../input/data-science-bowl-2019/test.csv")
train = pd.read_csv("../input/data-science-bowl-2019/train.csv")


def get_data(user_sample, test_set=False):
    all_assessments = []
    type_count = {'Clip':0, 'Activity': 0, 'Assessment': 0, 'Game':0}

    for i, session in user_sample.groupby('game_session', sort=False):
        session_title = session['title'].iloc[0]
        session_type = session['type'].iloc[0]

        if (session_type == 'Assessment') and (test_set or len(session) > 1):
            event_code = 4110 if session_title == 'Bird Measurer (Assessment)' else 4100
            all_attempts = session.query(f'event_code == {event_code}')
            num_correct = all_attempts['event_data'].str.contains('true').sum()
            num_incorrect = all_attempts['event_data'].str.contains('false').sum()

            features = {}
            features['installation_id'] = session['installation_id'].iloc[-1]
            features['title'] = session_title
            features['num_correct_attempts'] = num_correct

            features.update(type_count.copy())

            if test_set:
                all_assessments.append(features)
            elif num_correct + num_incorrect > 0:
                all_assessments.append(features)

        type_count[session_type] += 1

    if test_set:
        return all_assessments[-1]

    return all_assessments


compiled_train = []
for ins_id, user_sample in tqdm(train.groupby('installation_id', sort=False), total=17000):
    compiled_train += get_data(user_sample)

compiled_test = []
for ins_id, user_sample in tqdm(test.groupby('installation_id', sort=False), total=1000):
    test_data = get_data(user_sample, test_set=True)
    compiled_test.append(test_data)

X_train = pd.DataFrame(compiled_train)
X_test = pd.DataFrame(compiled_test)


特徴量作成の後にできたTrainデータとTestデータは次のようになっています。

display(X_train.head())
display(X_test.head())

heading of training and test dataframes


余分なinstallatio_idカラムは削除して、titleカラムのエンコーディングを行っておきましょう。

X_train = X_train.drop(['installation_id'], axis=1)
X_test = X_test.drop(['installation_id'], axis=1)

le = LabelEncoder()
le.fit(list(X_train['title'].values) + list(X_test['title'].values))
X_train['title'] = le.transform(list(X_train['title'].values))
X_test['title'] = le.transform(list(X_test['title'].values))


ここから先は上記のプロセスと同じくTrainデータとTestデータに新たな目的変数を与えて結合した後、TrainデータとTestデータが混ざったデータからそれぞれを分類していきます。

X_train['adv_target'] = 0
X_test['adv_target'] = 1
train_test_adv = pd.concat([X_train, X_test], axis=0).reset_index(drop=True)

train_adv, valid_adv = train_test_split(train_test_adv, test_size=0.33, random_state=42)
X_train_adv = train_adv.drop(['adv_target'], axis=1)
y_train_adv = train_adv['adv_target']
X_valid_adv = valid_adv.drop(['adv_target'], axis=1)
y_valid_adv = valid_adv['adv_target']

model = lgb.LGBMClassifier(
    n_estimators=1000,
    random_state=42)

model.fit(
    X_train_adv,
    y_train_adv,
    eval_set=[(X_train_adv, y_train_adv), (X_valid_adv, y_valid_adv)],
    eval_names=['train', 'valid'],
    eval_metric='auc',
    verbose=100)


ではモデルのパフォーマンスを見てみましょう。

ax = lgb.plot_metric(model.evals_result_, metric='auc')
plt.show()

plot of learing curve


Validationデータに対するAUCはおよそ0.93あたりで、TrainデータとTestデータの分布が同じ場合に比べモデルのパフォーマンスが良いことがわかります。これは今回用いたTrainデータとTestデータの分布が異なるため、モデルが容易に分類できるからです。

このようにして得られた結果から、TrainデータのうちTestデータである確率が高いと予測されたデータを取り出すことでTestデータに似たValidationデータを作成することが可能になります。またTrainデータとTestデータの分布が異なるかどうか見分ける指標としてAdversarial Validationを使うこともできます。


特徴量選択への応用

最後にAdversarial Validationを応用して特徴量選択を行う方法を紹介します。

まず先程のTrainデータとTestデータの分布が異なる場合に、予測に寄与した特徴量の重要度を見てみましょう。

plot of feature importance


これを見ると num correct attempts の重要度が高くなっています。これは num correct attempts がTrainデータかTestデータかの分類に大きく寄与していることを示しています。つまりTrainデータとTestデータの間でこの特徴量に乖離があることを意味しています。

これを受けて num correct attempts のカラムを削除してTrainデータとTestデータの分類を行ってみると、

new_X_train_adv = train_adv.drop(['num_correct_attempts'], axis=1)
new_X_valid_adv = valid_adv.drop(['num_correct_attempts'], axis=1)

model = lgb.LGBMClassifier(
    n_estimators=2000,
    random_state=42)

model.fit(
    X_train_adv,
    y_train_adv,
    eval_set=[(new_X_train_adv, y_train_adv), (new_X_valid_adv, y_valid_adv)],
    eval_names=['train', 'valid'],
    eval_metric='auc',
    verbose=100)

ax = lgb.plot_metric(model.evals_result_, metric='auc')
plt.show()

plot of learing curve


たしかにAUCスコアが低くなりTrainデータとTestデータの分類がしづらくなっていることがわかります。

このようにしてAdversarial Validationを応用することによって、TrainデータとTestデータで乖離している特徴量を見つけることができます。


まとめ

今回はKaggleなどの機械学習コンペで注目されているAdversarial Validationを紹介しました。Kaggleにおいて手元のバリデーションスコアとLeader Boardのスコアに乖離がある際に、適切なValidation setの作成や特徴量選択に役立ちます。実際に私もコンペで利用しており、今後ますます注目を浴びるのではないかと思われます。


Twitter・Facebookで定期的に情報発信しています!

1.判別分析の定義

判別分析とは、統計学上のデータ解析手法の一つである。
特定のグループに分かれているデータを元にどのような基準で判別されているか解析する。
そして、どちらのグループがA群に属され、B群に属されるかを予測する分析のことである。

2.判別分析の成り立ち

1936年にロナルドフィシャーによって線形判別分析が発表され、これを元に分析手法が発達した。

3.判別分析の例

1.医療診断 
・喫煙の有無により癌の発病を予測
・検査結果から病気の有無の判別
2.選挙予測
・世論調査による法案の通過の有無予測
・立候補政党から立候補者の当落の判別
3.受験合格の予想
・模擬試験の採点から志望校の合格を予測
・勉強時間等から合否の判別
4.マーケティングの予測
・企業の倒産の判別
・画像の色や形から異常の判別
・顧客の発注の有無判別

4.判別分析の種類

A 群とB群を線で仕切りA群に属するかBに属するかを分ける方法には、以下の2種類がある。
1.線形判別関数で仕切る
線形判別関数とは、2つのクラスを最もよく判別できる直線を求める手法の事である。

image

2.マハラノビス距離で仕切る
マハラノビス距離とは、データの分布の広がりを加味した距離のことで2点間の直線距離を 標準偏差で割った値を2乗して求められる距離のことである。

image

5.判別分析の手順

1.事例
例)A塾では、生徒をなるべくいい学校に合格させたい。
そこで生徒の能力で一番上の志望校をお勧めするために模試を作った。
2.分析の目的
模試によって生徒の志望校の合否判定を行うこと。
3.データ準備
今回の目的のために、様々なデータを集めた。
例)合格点数、志望者数、偏差値等のデータ
採用するデータを集める際には、人間が集めるためアナログな部分がある。
4.判別分析ツール
統計ソフトを使いそれらのデータを入れて計算する。
データは相関性の強いもののみを使用して、線形判別係数により判別式を作る。
5.分析結果の活用
判別式で出た数値を元に合否を判断する。
今回の場合、判別式でyが1以上であれば、合格になる可能性が高いと判断できる。 そして、A塾はこの模試の結果に基づいて生徒にもっとも良いと思われる学校をお勧めする。

6.まとめ

判別分析は、様々な場面で利用することができる。
しかし、データの選択をする際には、人間が選ぶため相関性が高いのか判別して式に落とし込む必要がある。

概要

ML.NETは、Microsoftが開発したクロスプラットフォーム対応の機械学習フレームワークである。 ML.NETで提供されているAutoMLを使うと、アルゴリズムの選択やオプションの設定などが自動で行われ、最も良い結果を出したモデルを得ることができる。 今回は、AutoMLによる文章の二項分類を行った。

環境

  • macOS Sierra 10.12.6
  • .NET SDK 2.2.107
  • ML.NET 0.3.27703.4

手順

.NET SDKのインストール

次のURLからインストーラをダウンロードし、インストールする。

Download .NET Core

ML.NET CLIのインストール

.NET SDKに含まれる dotnet コマンドを使用し、ML.NET CLIをインストールする。

$ dotnet tool install -g mlnet

AutoMLを用いたテキスト分類

1. 新規ディレクトリを作成する

$ mkdir myMLApp
$ cd myMLApp

2. Wikipedia detoxデータセットをダウンロードする

以下のようにしてデータセットをダウンロードする。

$ curl -O 'https://raw.githubusercontent.com/dotnet/machinelearning/master/test/data/wikipedia-detox-250-line-data.tsv'

wikipedia-detox-250-line-data.tsv は、文章が否定的(Sentiment=1)か肯定的(Sentiment=0)かがラベル付けされている。 データは以下のような形式になっている。

Sentiment   SentimentText
1           ==RUDE== Dude, you are rude upload that carl picture back, or else.
1           == OK! ==  IM GOING TO VANDALIZE WILD ONES WIKI THEN!!! 
0 I hope this helps.

3. 学習をする

与えられた文章に対して、Sentimentで二項分類するように学習する。 下記の mlnet auto-train コマンドを実行すると、様々なデータ変換、アルゴリズム、アルゴリズムのオプションが組み合わせて試された後、最も結果の良いものが出力される。

$ mlnet auto-train --task binary-classification --dataset "wikipedia-detox-250-line-data.tsv" --label-column-name "Sentiment" --max-exploration-time 10

オプションの意味は以下の通り。

  • --task: 機械学習の種類
  • --dataset: 使用するデータセット
  • --label-column-name: データセットの中で推論したいラベル
  • --max-exploration-time: ML.NETがモデルを探索する最大の秒数

実行すると以下のようなログが出力される。

Best Accuracy: 88.24%, Best Algorithm: LinearSvmBinary, Last Algorith...00:00:10

===============================================Experiment Results=================================================
------------------------------------------------------------------------------------------------------------------
|                                                     Summary                                                    |
------------------------------------------------------------------------------------------------------------------
|ML Task: binary-classification                                                                                  |
|Dataset: wikipedia-detox-250-line-data.tsv                                                                      |
|Label : Sentiment                                                                                               |
|Total experiment time : 10.42 Secs                                                                              |
|Total number of models explored: 33                                                                             |
------------------------------------------------------------------------------------------------------------------
|                                              Top 5 models explored                                             |
------------------------------------------------------------------------------------------------------------------
|     Trainer                              Accuracy      AUC    AUPRC  F1-score  Duration #Iteration             |
|1    LinearSvmBinary                        0.8824   0.7273   0.7848    0.9167       0.2         17             |
|2    SgdCalibratedBinary                    0.8235   0.7576   0.7857    0.8696       0.2          7             |
|3    SgdCalibratedBinary                    0.8235   0.7576   0.7857    0.8696       0.2         10             |
|4    SgdCalibratedBinary                    0.8235   0.7576   0.7857    0.8696       0.2         19             |
|5    SgdCalibratedBinary                    0.8235   0.7576   0.7857    0.8696       0.2         22             |
------------------------------------------------------------------------------------------------------------------

表の見出しの意味は次の通り。

  • Trainer: 学習に使われたアルゴリズム
  • Duration: 学習時間
  • #Iteration: 学習の反復回数

評価指標(Accuracy、AUC、AUPRC、F1-score)については以下を参照。

学習後、ディレクトリ構成は以下のようになる。

.
├── SampleBinaryClassification
│   ├── SampleBinaryClassification.ConsoleApp
│   │   ├── ModelBuilder.cs
│   │   ├── Program.cs
│   │   └── SampleBinaryClassification.ConsoleApp.csproj
│   ├── SampleBinaryClassification.Model
│   │   ├── DataModels
│   │   │   ├── ModelInput.cs
│   │   │   └── ModelOutput.cs
│   │   ├── MLModel.zip
│   │   └── SampleBinaryClassification.Model.csproj
│   ├── SampleBinaryClassification.sln
│   └── logs
│       └── debug_log.txt
└── wikipedia-detox-250-line-data.tsv

4. 学習したモデルを使用する

自動生成されるサンプルプロジェクトは、データファイルから一つの文章を取り出して推論をする内容となっている。 サンプルプロジェクトを実行するには以下のようにする。

$ cd SampleBinaryClassification/SampleBinaryClassification.ConsoleApp
$ dotnet run

出力結果

Single Prediction --> Actual value: True | Predicted value: True
=============== End of process, hit any key to finish ===============

参考

Windows 10でLinux環境を使いたい場合、Windows Subsystem for Linux(WSL)が便利です。WSL以外にもLinux環境を利用する手段はありましたが、以下のような点が不便でした。

Cygwin

・Windows向けにコンパイルされていないものは利用できない

・Windowsのコマンドを利用するときに、パスを変換する必要がある

foo.exe $(cygpath -w /path/to/file)

仮想マシン

・仮想マシンの起動に時間がかかる

・メモリーなどのリソースを、ホストとは別に用意する必要がある

WSLでは、これが以下のようになります。

・動作するソフトが多い WSLでは、Linuxの実行ファイルを直接実行できるため、多くのLinuxソフトを動かせます。

・WindowsとLinux間のコマンドの相互利用が簡単
パスを変換することなくWindowsのコマンドを実行できます。

foo.exe /path/to/file

また、 wsl コマンドによって、Windows側からLinuxのコマンドを実行することもできます。

wsl ls

・リソースをホストと共有する

一つのアプリケーションとして動くので、メモリーなどを別々に確保する必要がありません。 今回は、WSLでOpenCVのカスケード型分類器を動かしてみました。

環境

・Windows 10 Fall Creators Update 64bit

手順

WSLを有効にする

1.Windowsの設定画面から、「機能の有効化」と検索して、「Windowsの機能の有効化または無効化」を開く

img01.png

2.「Windows Subsystem for Linux」をチェックして、OKをクリックする。

img02.png

3.再起動を求められるので、そのまま再起動する。

UbuntuをWindowsストアからインストールする

1.Windowsストアを開く。

2.検索ボックスに「Linux」と入力して検索する。

3.検索結果の上部にLinuxの項目が出るので、「アプリを入手する」をクリックする。

img03.png

4.Ubuntuを選択して、インストールする。

img04.png

WindowsでOpenCVを導入する

1.OpenCVのサイトから、最新版の「Win pack」をダウンロードし、実行する。

https://opencv.org/releases.html

2.フォルダの展開先が聞かれるので、適当な場所を入力する。

img05.png

展開したフォルダの 「build/x64/vc14/bin」にパスを通しておく。

Ubuntuを起動する

1.初回起動時にはセットアップが行われるのでしばらく待つ。

2.新規ユーザーのユーザー名とパスワードを聞かれるので入力する。

Ubuntuでopencv-pythonをインストールする

pipを使ってopencv-pythonをインストールする。

$ pip3 install opencv-python

カスケード型分類器を動かす

1.学習データを用意する

ここではディレクトリ構成を下記のようにします。

.
├── cascade/
├── negative_image_list.txt
├── negative_images/
├── positive_image_list.txt
├── positive_images/
├── test_image.jpg
└── detect.py

positive_images/ negative_images/ にそれぞれ正解画像、不正解画像を入れておきます。 positive_image_list.txt には、以下のように正解画像のパスと画像内の物体の位置を記述します。

positive_image_list.txt

positive_images/000001.jpg 1 0 0 24 24
positive_images/000002.jpg 2 10 20 30 50 76 84 42 55

形式は以下のようになっています。

画像ファイルのパス 画像内の物体の個数 1つ目の物体のX座標 1つ目の物体のY座標 1つ目の物体のwidth 1つ目の物体のheight 2つ目の物体のX座標 ...

negative_image_list.txt には、以下のように不正解画像のパスを記述します。

negative_image_list.txt

negative_images/000001.jpg
negative_images/000002.jpg

2. 正解画像ファイルを作成する

下記コマンドにより、正解画像ファイルを作成します。 -num には、用意した正解画像の数を指定します。

$ opencv_createsamples.exe -info positive_image_list.txt -vec positive_sample.vec -num 1200

3. 学習を実行する

カスケード型分類器の学習を行います。 -numPos -numNeg には、それぞれ使用する正解画像、不正解画像の数を指定します。正解画像は、ここで指定する数よりも多くの画像を使用する場合があるので、用意した数よりも少ない数(8~9割程度)を指定する必要があります。

$ opencv_traincascade.exe -data cascade -vec positive_sample.vec -bg negative_image_list.txt -numPos 1000 -numNeg 500 -featureType LBP

学習が終了すると、 cascade/cascade.xml にカスケードファイルが生成されます。

4. 物体の検出を行う

以下のコードでは、画像内の物体を矩形で囲み、検出結果の画像を出力しています。

detect.py

import cv2

image_source = 'test_image.jpg'
image_dest = 'result_image.jpg'
cascade_path = 'cascade/cascade.xml'

cascade = cv2.CascadeClassifier(cascade_path)
image = cv2.imread(image_source)
objects = cascade.detectMultiScale(image, 1.3, 5)
for (x, y, w, h) in objects:
    image = cv2.rectangle(image, (x, y), (x + w, y + h), (255, 0, 0), 2)
cv2.imwrite(image_dest, image)
$ python3 detect.py

5. 結果を確認する

WSLではGUIを使えないので、Windows側から検出結果の画像 result_image.jpg を開いて確認します。

参考

Cascade Classifier Training -- OpenCV 2.4.13.4 documentation

https://www.docs.opencv.org/2.4/doc/user_guide/ug_traincascade.html

このアーカイブについて

このページには、過去に書かれた記事のうち分類カテゴリに属しているものが含まれています。

前のカテゴリはクラスタリングです。

次のカテゴリはです。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。