読者です 読者をやめる 読者になる 読者になる

ボンジニアの備忘録

凡人エンジニア、通称ボンジニアによる備忘録です。勉強した内容を書いていきます。ジャンル問わずです!間違っていたら指摘をお願いします!

機械学習に使うPythonのメモ

データの前処理

より良いトレーニングセットの構築

参考文献


# 欠損データの検索
import pandas as pd
from io import StringIO

csv_data = '''A,B,C,D
1.0,2.0,3.0,4.0
5.0,6.0,,8.0
10.0,11.0,12.0,
'''
df = pd.read_csv(StringIO(csv_data))
df.isnull().sum()
A    0
B    0
C    1
D    1
dtype: int64
# 欠損値を含む行を削除
df.dropna(axis=0)
A B C D
0 1.0 2.0 3.0 4.0
# 欠損値を含む列を削除
df.dropna(axis=1)
A B
0 1.0 2.0
1 5.0 6.0
2 10.0 11.0
# すべての列がNaNである行だけ削除
df.dropna(how='all')
A B C D
0 1.0 2.0 3.0 4.0
1 5.0 6.0 NaN 8.0
2 10.0 11.0 12.0 NaN
# 非NaN値が4つ未満の行を削除
df.dropna(thresh=4)
A B C D
0 1.0 2.0 3.0 4.0
# 特定の列にNaNが含まれているNaNが含まれている行だけ削除
df.dropna(subset=['C'])
A B C D
0 1.0 2.0 3.0 4.0
2 10.0 11.0 12.0 NaN

補完法

# 平均値補完
from sklearn.preprocessing import Imputer
# 欠損値補完のインスタンス生成(平均値補完)
imr = Imputer(missing_values='NaN', strategy='mean', axis=0)
# データを適合
imr = imr.fit(df)
# 補完実行
imputed_data = imr.transform(df.values)
pd.DataFrame(imputed_data)
0 1 2 3
0 1.0 2.0 3.0 4.0
1 5.0 6.0 7.5 8.0
2 10.0 11.0 12.0 6.0

カテゴリーデータの処理

import pandas as pd
# sapmle data
df = pd.DataFrame([
        ['green','M',10.1,'class1'],
        ['red','L',13.5,'class2'],
        ['blue','XL',15.3,'class1']
    ])
# 列名の変更
df.columns = ['color','size','price','classlabel']
df
color size price classlabel
0 green M 10.1 class1
1 red L 13.5 class2
2 blue XL 15.3 class1
# 順序特徴量のマッピング
# Tシャツのサイズと整数を対応させる
size_mapping = {'XL':3, 'L':2, 'M':1}
# Tシャツのサイズを整数に変換
df['size'] = df['size'].map(size_mapping)
df
color size price classlabel
0 green 1 10.1 class1
1 red 2 13.5 class2
2 blue 3 15.3 class1
import numpy as np
# クラスラベルと整数を対応させるディクショナリーを作成
class_mapping = {label:idx for idx, label in enumerate( np.unique(df['classlabel']) )}
class_mapping
df['classlabel'] = df['classlabel'].map(class_mapping)
df
color size price classlabel
0 green 1 10.1 0
1 red 2 13.5 1
2 blue 3 15.3 0
inv_class_mapping = {v: k for k, v in class_mapping.items()}
df['classlabel'] = df['classlabel'].map(inv_class_mapping)
df
color size price classlabel
0 green 1 10.1 class1
1 red 2 13.5 class2
2 blue 3 15.3 class1
# クラスラベルを整数に変換
from sklearn.preprocessing import LabelEncoder
class_le = LabelEncoder()
y = class_le.fit_transform(df['classlabel'].values)
y
array([0, 1, 0])
# クラスラベルを元に戻す
class_le.inverse_transform(y)
array(['class1', 'class2', 'class1'], dtype=object)
# 名義特徴量のエンコーディング
from sklearn.preprocessing import OneHotEncoder
X = df[['color','size','price']].values
color_le = LabelEncoder()
X[:,0] = color_le.fit_transform(X[:,0])
# one-hot エンコーダの生成
ohe = OneHotEncoder(categorical_features=[0])
# one-hotエンコーディングの実施
ohe.fit_transform(X).toarray()
array([[  0. ,   1. ,   0. ,   1. ,  10.1],
       [  0. ,   0. ,   1. ,   2. ,  13.5],
       [  1. ,   0. ,   0. ,   3. ,  15.3]])
pd.get_dummies(df[['color','size','price']])
size price color_blue color_green color_red
0 1 10.1 0.0 1.0 0.0
1 2 13.5 0.0 0.0 1.0
2 3 15.3 1.0 0.0 0.0

4.5 有益な特徴量の選択

過学習を減らすために必要な事 * さらに多くのトレーニングデータを集める * 正則化を通じて複雑さにペナルティを科す * パラメータの数が少ない、より単純なモデルを選択する * データの次元の数を減らす

L2正則化がモデルの複雑さを低減する方法の一つ L1正則化 無関係な特徴量の個数が多い高次元のデータセットがある






import pandas as pd
df_wine = pd.read_csv('https://archive.ics.uci.edu/ml/machine-learning-databases/wine/wine.data',header=None)
df_wine.columns = ['Class label','Alcohol', 'Malic acid', 'Ash', 'Alcalinity of ash','Magnesium',
                   'Total phenols', 'Flavanoids','Nonflavanoid phenols','Proanthocyanins','Color intensity','Hue','OD','Proline']
from sklearn.cross_validation import train_test_split
X, y = df_wine.iloc[:,1:].values, df_wine.iloc[:,0].values
X_train, X_test, y_train,y_test = train_test_split(X, y, test_size=0.3, random_state=0)
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
stdsc = StandardScaler()
X_train_std = stdsc.fit_transform(X_train)
X_test_std = stdsc.fit_transform(X_test)
lr = LogisticRegression(penalty='l1', C=0.1)
lr.fit(X_train,y_train)
print('Training accuracy:', lr.score(X_train_std,y_train))
lr.coef_
Training accuracy: 0.862903225806





array([[ 0.        ,  0.        ,  0.        , -0.39570919, -0.04408174,
         0.        ,  0.        ,  0.        ,  0.        ,  0.        ,
         0.        ,  0.        ,  0.0144286 ],
       [ 0.05863568, -0.01814456,  0.        ,  0.1203236 ,  0.04329638,
         0.        ,  0.        ,  0.        ,  0.        , -0.92300575,
         0.        ,  0.        , -0.00569571],
       [ 0.        ,  0.        ,  0.        ,  0.        , -0.00170408,
         0.        , -1.14833783,  0.        ,  0.        ,  0.67990963,
         0.        ,  0.        , -0.00322748]])
%matplotlib inline
import matplotlib.pyplot as plt
fig = plt.figure()
ax = plt.subplot(111)
colors = ['blue','green','red','cyan','magenta','yellow','black','pink','lightgreen','lightblue','gray','indigo','orange']
weights, params = [],[]

for c in np.arange(-4, 6):
    lr = LogisticRegression(penalty='l1', C=10**c, random_state=0)
    lr.fit(X_train_std,y_train)
    weights.append(lr.coef_[1])
    params.append(10**c)
    
weights = np.array(weights)
for column, color in zip(range(weights.shape[1]), colors):
    plt.plot(params, weights[:, column], label=df_wine.columns[column + 1], color=color)

plt.axhline(0, color='black', linestyle='--', linewidth=3)
plt.xlim([10**(-5), 10**5])
plt.ylabel('weight coefficient')
plt.xlabel('C')
plt.xscale('log')
plt.legend(loc='upper left')
ax.legend(loc='upper center', bbox_to_anchor=(1.38,1.03), ncol=1,fancybox=True)
plt.show()

png

4.5.2 逐次特徴選択アルゴリズム

次元削減法は主に二つのカテゴリーに分かれます。 * 特徴選択
元の特徴量の一部を選択する。 * 特徴抽出
新しい特徴部分空間を生成するために特徴量の集合から情報を抽出する。

逐次特徴選択アルゴリズムは貪欲探索アルゴリズムの一種
貪欲探索アルゴリズムは元々の「d次元」の特徴空間を「k次元」の特徴部分空間に削減するために使用される(k<d)

目的
* 特徴量の部分集合を自動的に選択することで、計算効率の改善。 * 無関係な特徴量やノイズを取り除くことで、モデルの汎化誤差を削減すること

逐次後退選択(SBS)は逐次特徴選択アルゴリズムで、その目的は元々の特徴空間の次元を減らすことにあり、分類機の性能の低下を最小限に抑えた上で計算効率を改善する。

# SBS
from sklearn.base import clone
from itertools import combinations
import numpy as np
from sklearn.cross_validation import train_test_split
from sklearn.metrics import accuracy_score

class SBS():
    """
    逐次後退選択を実行するクラス
    """

    def __init__(self, estimator, k_features, scoring=accuracy_score,test_size=0.25, random_state=1):
        self.scoring = scoring
        self.estimator = clone(estimator)
        self.k_features = k_features
        self.test_size = test_size
        self.random_state = random_state
        
    def fit(self, X, y):
        # トレーニングデータとテストデータに分割
        X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=self.test_size, random_state=self.random_state)
        # すべての特徴量の個数、列インデックス
        dim = X_train.shape[1]
        self.indices_ = tuple(range(dim))
        self.subsets_ = [self.indices_]
        # すべてのスコアの特徴量を用いてスコアを算出
        score = self._calc_score(X_train, y_train, X_test, y_test, self.indices_)
        
        # スコアを格納
        self.scores_ = [score]
        # 指定した特徴量の個数になるまで処理を反復
        while dim > self.k_features:
            # 空のリストの生成(スコア、列インデックス)
            scores = []
            subsets = []
            
            # 特徴量の部分集合を表す列インデックスの組み合わせごとに処理を反復
            for p in combinations(self.indices_ , r=dim-1):
                # スコアを算出して格納
                score = self._calc_score(X_train, y_train, X_test, y_test, p)
                scores.append(score)
                # 特徴量の部分集合を表す列インデックスのリストを格納
                subsets.append(p)
                
            # 最良のスコアのインデックスを抽出
            best = np.argmax(scores)
            # 最良のスコアとなる列インデックスを抽出して格納
            self.indices_ = subsets[best]
            self.subsets_.append(self.indices_)
            # 特徴量の個数を1つだけ減らして次のステップへ
            dim -= 1
            
            # スコアを格納
            self.scores_.append(scores[best])
            
        self.k_score_ = self.scores_[-1]
        
        return self
    
    def transform(self, X):
        # 抽出した特徴量を返す
        return X[:, self.indices_]
    
    def _calc_score(self, X_train, y_train, X_test, y_test, indices):
        # 指定された列番号indicesの特徴量を抽出してモデルに適合
        self.estimator.fit(X_train[:, indices], y_train)
        # テストデータを用いてクラスラベルを予測
        y_pred = self.estimator.predict(X_test[:, indices])
        # 真のクラスラベルと予測値を用いてスコアを算出
        score = self.scoring(y_test, y_pred)
        return score
from sklearn.neighbors import KNeighborsClassifier
import matplotlib.pyplot as plt
%matplotlib inline
# k近傍分類器のインスタンスを生成
knn = KNeighborsClassifier(n_neighbors=2)
# 逐次後退選択のインスタンスを生成
sbs = SBS(knn, k_features=1)
# 逐次後退選択を実行
sbs.fit(X_train_std,y_train)
# 近傍点の個数リスト
k_feat = [len(k) for k in sbs.subsets_]
plt.plot(k_feat, sbs.scores_, marker='o')
plt.ylim([0.7, 1.1])
plt.ylabel('Accuracy')
plt.xlabel('Number of feature')
plt.grid()
plt.show()

k5 = list(sbs.subsets_[8])
print(df_wine.columns[1:][k5])