k-最近傍法 – 回帰

概要

k-最近傍法(k nearest neighbors: knn)による回帰は、テストデータの近傍の訓練データからテストデータの属性値を決定する。その手法は単純で、特段の学習処理はせず、訓練データセットの特徴量と属性値を記憶するのみで、テストデータが与えられたときに近傍点から属性値を決定する。手順は以下の通り。

  1. パッケージをインポートする
  2. 特徴量と属性値のデータセットを記憶する
  3. テストデータが与えられたら、特徴量空間の中で近傍点を選ぶ
  4. 近傍点の属性値からテストデータの属性値を決定する

パラメーターは近傍点の数で、1以上訓練データの数まで任意に増やすことができる。

利用方法

手順

scikit-learnのKNeighborsRegressorクラスの利用方法は以下の通り。

  1. sklearn.neighborsからKNeighborsRegressorをインポート
  2. コンストラクターの引数に近傍点数n_neighborsを指定して、KNeighborsRegressorのインスタンスを生成
  3. fit()メソッドに訓練データの特徴量と属性値を与えて学習
  4. predict()メソッドにテストデータの特徴量を指定して、属性値を予測
  5. 必要に応じて、kneighbors()メソッドでテストデータの近傍点情報を取得

パッケージのインポート

k-最近傍回帰のパッケージは以下でインポートする。

コンストラクター

KNeighborsClassifier(n_neighbors=n)
nは近傍点の数でデフォルトは5。この他の引数に、近傍点を発見するアルゴリズムなどが指定できるようだ。

訓練

fit()メソッドに与える訓練データは、特徴量セットと属性値の2つ。

fit(X, y)
Xは訓練データセットの特徴量データで、データ数×特徴量数の2次元配列。yは訓練データセットの属性値データで要素数はデータ数に等しい

予測

テストデータの属性値の予測は、predict()メソッドにテストデータの特徴量を与える。

y = predict(X)
Xはテストデータの特徴量データで、データ数×特徴量数の2次元配列。戻り値yは予測された属性値データで要素数はデータ数に等しい。

近傍点の情報

テストデータに対する近傍点の情報を、kneighbors()メソッドで得ることができる。

neigh_dist, neigh_ind = kneighbors(X)
テストデータの特徴量Xを引数に与え、近傍点に関する情報を得る。neigh_distは各テストデータから各近傍点までの距離、neigh_indは各テストデータに対する各近傍点のインデックス。いずれも2次元の配列で、テストデータ数×近傍点数の2次元配列となっている。

実行例

以下の例では、n_neighbors=2としてKNeighborsRegressorのインスタンスを準備している。

これに対してfit()メソッドで、2つの特徴量とそれに対する属性値を持つ訓練データを5個与えている。特徴量データX_trainは行数がデータ数、列数が特徴量の数となる2次元配列を想定している。また属性値y_trainは訓練データ数と同じ要素数の1次元配列。

特徴量1 特徴量2 属性値
-2 -3 -1
-1 -1 0
0 1 1
1 2 2
3 3 3

これらの訓練データに対して、テストデータの特徴量X_testとして(-0.5, -2)(1, 0)の2つを与えた時の出力を見てみる。

このコードの実行結果は以下の通り。

属性値の予測結果については、2つのテストデータに対して2つの属性値0.5と1.5が返されている。

kneighbors()メソッドの戻り値から、1つ目のテストデータにはインデックスが1と0の2つの点とそれぞれへの距離1.118と1.802が、2つ目のテストデータにはインデックスが2と3の点とそれぞれへの距離1.414と2.0が得られる。

  • 1つ目のテストデータ(-0.5, -2)からの距離
    • X_train[1]=(-1, -1)\sqrt{(-0.5)^2+1^2}\approx 1.118
    • X_train[0]=(-2, -3)\sqrt{(-1.5)^2+(-1)^2}\approx 1.802
  • 2つ目のテストデータ(1, 0)からの距離
    • X_train[2]=(0, 1)\sqrt{(-1)^2+1^2}\approx 1.414
    • X_train[3]=(1, 2)\sqrt{0^2+2^2}=2

y_predは、テストデータごとに2つの近傍点の属性値の平均をとっている。

  • 1つ目のテストデータの属性値
    • y_train[1]=-1y_train[0]=0の平均→-0.5
  • 2つ目のテストデータの属性値
    • y_train[2]=1y_train[3]=2の平均→1.5

この様子を特徴量平面上に描いたのが以下の図である。各点の数値は、各データの属性値を示している。

各種データに対する適用例

 

waveデータセット

概要

waveデータセットは、”Pythonではじめる機械学習”(O’REILLY)中で用いられる架空のデータセットである。

その内容は、引数n_samplesで指定した個数の点について1つの特徴量とターゲットの値を持ち、回帰を扱うのに適している。

利用方法

mglearnパッケージから、たとえば以下のように利用する。

実行するとdeprecatedの警告が出るが、放置してもよいらしい。

内容

waveデータの特徴は以下の通り。

  • 引数のn_samplesには任意の整数を指定できる
  • 特徴量(x座標の値)は決まっている
    • n_samplesが増えてもx0, x1, …の値は変わらない
    • x0, x1, …は実行のたびに同じパターン
  • ターゲットの値(y座標の値)は変化するが実行ごとに同じ
    • n_samplesが変わると同じx0, x1, …の値に対するy0, y1, …の値は変化する
    • y0, y1, …は実行のたびに同じパターン

このことを、n_samplesの値を変化させたときのX, yの内容で確認してみる。

このコードは何度実行しても同じ値を返す。x座標のパターンが変わっていないこと、y座標のパターンは実行のたびに変化していることがわかる。ただし異なるn_sampleに対して、同じxに対するyの値は大きくは変化していない。

なお、n_samplesが6の時のxの最後の値とその1つ前の値がかなり近く、対応するyの値も近い。n_samplesが1の時と3の時に、先頭のXとyの値が殆ど等しい。

以上のことから、waveデータセットはXについては毎回同じ系列でランダムな値を返し、yはXに対して一定の計算値に毎回同じ系列の乱数で擾乱を加えていると想像される。

最後に、n_samplesを多くしたときの結果を見てみると明らかに線形で上昇しつつ波打っているのがわかる。おそらくy=a \sin b x + cのような式に擾乱を与えていると思われる。

手法の適用

 

Breast cancer データセット – k-近傍法

概要

breast_cancerデータセットにscikit-learnのKNeighborsClassifierクラスでk-最近傍法を適用した結果。

学習率曲線

breast_cancerデータセットにk-最近傍法を適用し、近傍点数を変化させて学習率の変化をチェック。データセットを学習データとテストデータに分けるときのrandom_stateを変え、近傍点数に伴う変化を見てみた。

irisデータセットの場合に比べると、学習データとテストデータの傾向は落ち着いていて、近傍点数=8で制度が0.92~0.95程度。

 

irisデータセット – knn

概要

irisデータセットにscikit-learnのKNeighborsClassifierクラスでk-最近傍法を適用した結果。

学習率曲線

irisデータセットにk-最近傍法を適用し、近傍点数を変化させて学習率の変化をチェック。データセットを学習データとテストデータに分けるときのrandom_stateを変え、近傍点数に伴う変化を見てみた。

レコード数が150と少ないこともあって、random_stateを変えるごとにかなり推移が異なるが、概ね95%の精度が保たれている。

 

Breast cancer wisconsinデータセットの俯瞰

概要

breast_cancerデータは、複数の乳癌患者に関する細胞診の結果と診断結果に関するデータセットで、569人について腫瘤の細胞診に関する30の特徴量と診断結果(悪性/良性)が格納されている。このデータセットについて、irisデータセットと同じ流れで、一般的なグラフによる可視化によって俯瞰してみる。

各特徴量と診断結果

30個の特徴量について悪性と良性に色分けしてヒストグラムを描いてみると、特徴量によって悪性と良性がある程度分かれているものと、重なりが大きいものがあることがわかる。

特徴量の数は多いが、低い次元で見る限りは明確に悪性/良性を分離できる特徴量はあまり多くなさそうである。

2つの特徴量同士の関係

特徴量が30個あるので、scatter_matrixやpairplotで全ての特徴量の関係を見るのはあまり得策ではない。そこで、30個の特徴量の中から、悪性/良性が分かれているものを選んで相互の関係を見てみる。

ここでは、双方の分布の山ができるだけ離れており、重なっている部分が少ないものとして、平均凹度、最大半径、最大周囲長、最大凹点数を選んだ。

最大半径と最大周囲長はかなり相関が高く、双方を組み合わせてもあまり効果はなさそうだ。もともと半径と周囲長は円形なら比例関係にあるので当然の結果だろう。

 

3つの特徴量の関係

最後に、平均凹度、最大半径、最大凹点数の3つの特徴量の関係を3次元化してみた。結果の図を回転させて、できるだけ境界面に沿うような角度から見たのが以下の図である。個々の特徴量だけで見るよりはかなり分離の精度は高くなっている。

上記の3d可視化とその前のpairplotのコードは下記の通り。

 

Breast cancer wisconsinデータセット

概要

breast cancerデータはUCIの機械学習リポジトリ―にあるBreast Cancer Wisconsin (Diagnostic) Data Setのコピーで、乳腺腫瘤の穿刺吸引細胞診(fine needle aspirate (FNA) of a breast mass)のデジタル画像から計算されたデータ。

乳癌に関する細胞テストの様々な数値と、その結果のデータセット。569人の被検者の複数の腫瘤に関する細胞診の結果得られた30個の特徴量と、各被験者の診断結果(悪性/良性:benign/malignant)が格納されている。

ここではPythonのscikit-learnにあるbreast_cancerデータの使い方をまとめる。

データの取得とデータ構造

Pythonで扱う場合、scikit-learndatasetsモジュールにあるload_breast_cancer()でデータを取得できる。データはBunchクラスのオブジェクト。

データの構造は辞書型で、569人の細胞診の結果に関する30個の特徴量をレコードとしたの配列、各腫瘤の診断結果など。

データのキーは以下のようになっている。

データの内容

'data'~特徴量データセット

569人の細胞診結果に対する30個の特徴量のデータを格納した2次元配列。列のインデックス(0~29)が30個の特徴量に対応している。

'target'~診断結果に対応したコード

各被検者の診断結果(悪性:malignant、良性:benign)を格納した0/1のコードの配列。569個の腫瘤に対応した1次元配列(0:悪性が212、1:良性が357)。

'target_names'~診断結果

診断結果、悪性(malignant)/良性(benign)が定義されている。。

診断結果とコードの関係は以下の通り。

malignant 0
benign 1

'feature_names'~特徴名

データの格納順はDESCRの後。細胞診の結果得られた30個の特徴量の名前。

腫瘤に関する以下の10の属性について、それぞれ平均(mean)、標準偏差(error)、最悪値(worst)の3種類、合計30の特性値に対する名前が格納されている。ここでworstは各属性に関する最大値となっている。

  1. radius:半径(中心から外周までの平均)
  2. texture:テクスチャ―のグレースケールの標準偏差
  3. perimeter:外周長
  4. area:面積
  5. smoothness:中心から外周までの部分偏差
  6. compactness:コンパクト性(外周長2÷面積-1.0)
  7. concavity:コンターの凹部強度
  8. concave points:コンターの凹点の数
  9. symmetry:対称性
  10. fractal dimension:フラクタル次元

特徴名とコードの関係は以下の通り。

mean error worst
radius 0 10 20
texture 1 11 21
perimeter 2 12 22
area 3 13 23
smoothness 4 14 24
compactness 5 15 25
concavity 6 16 26
concave points 7 17 27
symmetry 8 18 28
fractal dimension 9 19 29

'filename'~ファイル名

これも格納順はDESCRの後で、CSVファイルの位置が示されている。1行目にはデータ数、特徴量数、特徴量名称が並んでおり、その後に569行のレコードに対する4列の特徴量と1列の診断結果データが格納されている。このファイルにはfeature_namesDESCRに当たるデータは格納されていない。

'DESCR'~データセットの説明

データセットの説明。print(breast_ds_dataset['DESCR'])のようにprint文で整形表示される。

  • レコード数569個(悪性:212、良性:357)
  • 属性は、30の数値属性とクラス
    →predictiveの意味とclassが単数形なのがわからない

データの利用

データの取得方法

breast_cancerデータセットから各データを取り出すのに、以下の2つの方法がある。

  • 辞書のキーを使って呼び出す(例:breast_cancer_dataset['DESCR']
  • キーの文字列をプロパティーに指定する(例:breast_cancer_dataset.DESCR

全レコードの特徴量データの取得

'data'から、569のレコードに関する30の特徴量が569行30列の2次元配列で得られる。30の特徴量は'feature_names'の30の特徴名に対応している。

特定の特徴量のデータのみ取得

特定の特徴量に関する全レコードのデータを取り出すときにはX[:, n]の形で指定する。

特定のクラスのデータのみ抽出

特定のクラス(この場合は診断結果)のレコードのみを抽出する方法。ndarrayの条件による要素抽出を使う。

 

 

k-最近傍法 – クラス分類

概要

k-最近傍法(k nearest neighbors: knn)によるクラス分類は、テストデータの近傍の訓練データからテストデータのクラスを決定する。その手法は単純で、特段の学習処理はせず、訓練データの特徴量とクラスを記憶するのみで、テストデータが与えられたときに近傍点からクラスを決定する。手順は以下の通り。

  1. 特徴量とクラス分類の訓練データセットを記憶する
  2. テストデータが与えられたら、特徴量空間の中で近傍点を選ぶ
  3. 近傍点のクラスからテストデータのクラスを決定する

パラメーターは近傍点の数で、1以上の自然数を設定できる。

利用方法

scikkit-learnのKNeighborsClassifierクラスの利用方法は以下の通り。

  1. sklearn.neighborsからKNeighborsClassifierをインポート
  2. コンストラクターの引数に近傍点数n_neighborsを指定して、KNeighborsClassifierのインスタンスを生成
  3. fit()メソッドに訓練データの特徴量と属性値を与えて学習
  4. predict()メソッドにテストデータの特徴量を指定して、属性値を予測
  5. 必要に応じて、kneighbors()メソッドでテストデータの近傍点情報を取得

コンストラクターには、通常n_neighborsで近傍点を指定する。デフォルトはn_neighbors=5

KNeighborsClassifier(n_neighbors=n)
nは近傍点の数。この他の引数に、近傍点を発見するアルゴリズムなどが指定できるようだ。

fit()メソッドに与える訓練データは、特徴量セットと属性値の2つ。

fit(X, y)
Xは訓練データセットの特徴量データで、データ数×特徴量数の2次元配列。yは訓練データセットのクラスデータで要素数はデータ数に等しい

テストデータの属性値の予測は、predict()メソッドにテストデータの特徴量を与える。

y = predict(X)
Xはテストデータの特徴量データで、データ数×特徴量数の2次元配列。戻り値yは予測されたクラスデータで要素数はデータ数に等しい。

テストデータに対する近傍点の情報を、kneighbors()メソッドで得ることができる。

neigh_dist, neigh_ind = kneighbors(X)
X_testはテストデータの特徴量データで、データ数×特徴量数の2次元配列。戻り値y_testは予測された属性値データで要素数はデータ数に等しい。
neigh_dist, neigh_ind = kneighbors(X)
テストデータの特徴量Xを引数に与え、近傍点に関する情報を得る。neigh_distは各テストデータから各近傍点までの距離、neigh_indは各テストデータに対する各近傍点のインデックス。いずれも2次元の配列で、テストデータ数×近傍点数の2次元配列となっている。

実行例

以下の例では、n_neighbors=2としてKNeighborsClassifierのインスタンスを準備している。

これに対してfit()メソッドで、2つの特徴量とそれに対するクラス値を持つ訓練データを6個与えている。特徴量データX_trainは行数がデータ数、列数が特徴量の数となる2次元配列を想定している。また属性値y_trainは訓練データ数と同じ要素数の1次元配列。

特徴量1 特徴量2 クラス値
-2 -1 0
-1 -2 0
-0.5 -0.5 0
0.5 0.5 1
1 2 1
2 1 1

これらの訓練データに対して、テストデータの特徴量X_testとして(-0.5, -1.5)(1, 0)の2つを与えた時の出力を見てみる。

このコードの実行結果は以下の通り。

属性値の予測結果については、2つのテストデータに対して2つのクラス値0と1が返されている。

kneighbors()メソッドの戻り値から、1つ目のテストデータにはインデックスが1, 2, 0の3つの点とそれぞれへの距離0.7071, 1, 1.5811が、2つ目のテストデータにはインデックスが3, 5, 2の点とそれぞれへの距離0.7071, 1.4142, 1.5811が得られる。

  • 1つ目のテストデータ(-0.5, -1.5)からの距離
    • X_train[1]=(-1, -2)\sqrt{(-0.5)^2+(-0.5)^2}\approx 0.7071
    • X_train[2]=(-0.5, -0.5)\sqrt{0^2+(-1)^2} = 1
    • X_train[0]=(-2, -1)\sqrt{(-1.5)^2+0.5^2} \approx 1.5811
  • 2つ目のテストデータ(1, 0)からの距離
    • X_train[3]=(0.5, 0.5)\sqrt{(-0.5)^2+0.5^2}\approx 0.7071
    • X_train[5]=(2, 1)\sqrt{1^2+1^2}\approx 1.4142
    • X_train[2]=(-0.5, -0.5)\sqrt{(-1.5)^2+(-0.5)^2} \approx 1.5811

y_predは、テストデータごとに2つの近傍点のクラス値から多数決でクラス値を決定している。

  • 1つ目のテストデータの属性値
    • y_train[1]=0y_train[2]=0y_train[0]=0の多数決→0
  • 2つ目のテストデータの属性値
    • y_train[3]=1y_train[5]=1y_train[2]=0の多数決→1

この様子を特徴量平面上に描いたのが以下の図である。各点の色は、各データのクラスを示していて、下方の点は3つの近傍点のクラスが全て0なのでテストデータのクラスも0、右方の点は近傍点のうち2つがクラス1で1つがクラス0なのでテストデータのクラスは多数決で1となっている様子がわかる。

各種データに対する適用例

 

forgeデータセット

概要

forgeデータセットは、”Pythonではじめる機械学習”(O’REILLY)中で用いられる架空のデータセットである。

その内容は、2クラスに分類された26個のデータで、2つの特徴量を想定した2次元配列データと各データのクラス分類を示したターゲットデータが得られる。

利用方法

mglearnパッケージから、たとえば以下の方法で利用する。

実行するとdeprecatedの警告が出るが、放置してもよいらしい。

内容

特徴量データ

2つの特徴量を持った配列が26個、2次元配列の形で得られる。

ターゲットデータ

26個のデータに対する2つのクラス(0, 1)が定められた1次元配列で、クラス0、1がそれぞれ13個ずつとなっている。

データ総括

特徴量とクラス分類の組を、データ番号とともに整理すると以下の通り。

 

訓練データとテストデータの分割~train_test_split()

概要

scikit-learnのtrain_test_split()関数を使うと、与えたデータをいろいろな方法で訓練データとテストデータに切り分けてくれる。

8行目で、train_test_split()に配列を与えた結果、それが2つの配列に分割されていることがわかる。

11行目では、その結果を訓練用、テスト用の配列として取得している。

デフォルトでtrain_test_split()は、テスト用データのサイズが与えた配列のサイズの0.25となるように配列を分割する(1つ目のサイズ:2つ目のサイズ=3:1)。x_testのサイズが12×0.25=3、x_trainのサイズが9となっていることが確認できる。

乱数系列の固定

データの分割あたって、要素の選択はtrain_test_split()の実行ごとにランダムに行われるが、random_stateパラメーターを指定することで固定できる。

データのサイズ

テストデータサイズの指定

テストデータのサイズはtest_sizeパラメーターで指定することができる。

以下の例では、テストデータの比率をデフォルトの0.25→0.3に変更しており、テストデータのサイズが4となっている(test_size=0.26としてもx_testのサイズが4になり、テストデータのサイズは切り上げで計算されている)。

比率によってデータサイズを指定する場合は0<test_size<1の実数で指定(0や1.0で指定するとエラー)

訓練データのサイズを比率ではなく実際のサイズ(要素数)で指定することもできる。その場合、test_sizeを1以上の整数で指定。

以下の例ではテストデータのサイズを4として指定している。

訓練データサイズの指定

train_sizeパラメーターで訓練データのサイズを指定することもできる。

以下の例ではtrain_size=0.8とし、訓練データサイズが9となっている(訓練データサイズの計算は切り下げで行われている)。

訓練データサイズも要素数での指定が可能。

データ選択の内部手続

ここで、random_state=0としてtest_sizetrain_sizeを変化させたとき、テストデータの要素が現れる順番は変わらないということに気づいた。

test_size/train_sizeのどちらで指定しても、また比率/要素数の何れで指定しても、常にテストデータの要素は7, 12, 5,…の順番で現れている。

これに対して訓練データの方は、テストデータの要素数が変わると変化するが、テストデータの結果が同じなら訓練データのパターンも同じ。

すなわちtrain_test_split()のサイズ指定は、どのように指定しても一旦テストデータの要素数に変換し、共通の手順でテストデータを選んでいっていると考えられる。

複数データの同時分割

train_test_split()は複数データを同時に分割することもできる。

以下の例では、二つの配列を引数として与えている。その結果は、与えた配列ごとに訓練データ、テストデータの順でタプルとして返される。

これが一般的な使い方で、複数の特徴量に関する個体のデータセットと各個体のクラスに関するデータを、同時に訓練データとテストデータに分割するときに用いられる。

元のデータは

これを訓練データとテストデータに分割した結果は

stratifyによる層化(相似化)

train_test_split()による要素の選択はランダムに行われる。この場合、クラス分類のパターンが、元データ、訓練データ、テストデータで異なってくる。

以下の例では、元のデータの0と1の比率が1:2だが、訓練データでは1:4、テストデータでは2:1になっている。ケースによっては特定のクラスが極端に少ない/存在しないということも起こり得る。

そこで、stratifyパラメーターで配列を指定すると、その配列でのパターンと同じになるように訓練データとテストデータを分割してくれる。

以下の例では、先の配列を元の配列の0/1のパターンと相似になるように分割している。

次の例は、9個体の特徴量データxと各個体のクラス区分データyを、クラスの分布に沿って訓練データとテストデータに分割するイメージ。

シャッフルの有無

デフォルトでtrain_test_split()は、データの分割にあたって要素の選択をランダムに行うが、shuffle=Falseを指定すると要素の順番を保持する。