hacktricks/a.i.-exploiting/bra.i.nsmasher-presentation/ml-basics/feature-engineering.md

20 KiB
Raw Blame History

ゼロからヒーローまでのAWSハッキングを学ぶ htARTEHackTricks AWS Red Team Expert

HackTricks をサポートする他の方法:

可能なデータの基本的なタイプ

データは 連続無限の値)または カテゴリカル(名義)であり、可能な値の量が 限られている場合があります。

カテゴリカルタイプ

2値

2つの可能な値 のみ: 1 または 0。データセット内の値が文字列形式である場合例: "True" と "False")、これらの値に数値を割り当てる場合は:

dataset["column2"] = dataset.column2.map({"T": 1, "F": 0})

序数

値は順序に従います、例えば: 1位、2位... カテゴリが文字列の場合(例: "初心者"、"アマチュア"、"プロフェッショナル"、"エキスパート")は、バイナリの場合と同様にそれらを数値にマッピングできます。

column2_mapping = {'starter':0,'amateur':1,'professional':2,'expert':3}
dataset['column2'] = dataset.column2.map(column2_mapping)
  • アルファベットの列の場合、より簡単に並べ替えることができます:
# First get all the uniq values alphabetically sorted
possible_values_sorted = dataset.column2.sort_values().unique().tolist()
# Assign each one a value
possible_values_mapping = {value:idx for idx,value in enumerate(possible_values_sorted)}
dataset['column2'] = dataset.column2.map(possible_values_mapping)

サイクリカル

序数値のように見えますが、順序があるため、一方が他よりも大きいという意味ではありません。また、それらの間の距離は、数え方の方向に依存します。例:曜日、日曜日は月曜日よりも「大きい」わけではありません。

  • サイクリカル特徴をエンコードする異なる方法があり、いくつかのアルゴリズムだけで動作する場合があります。一般的には、ダミーエンコードを使用できます
column2_dummies = pd.get_dummies(dataset.column2, drop_first=True)
dataset_joined = pd.concat([dataset[['column2']], column2_dummies], axis=1)

日付

日付は連続変数です。繰り返されるため、循環的な変数として見ることができます。また、順序変数として見ることもできます(時間は前の時間よりも大きいため)。

  • 通常、日付はインデックスとして使用されます。
# Transform dates to datetime
dataset["column_date"] = pd.to_datetime(dataset.column_date)
# Make the date feature the index
dataset.set_index('column_date', inplace=True)
print(dataset.head())

# Sum usage column per day
daily_sum = dataset.groupby(df_daily_usage.index.date).agg({'usage':['sum']})
# Flatten and rename usage column
daily_sum.columns = daily_sum.columns.get_level_values(0)
daily_sum.columns = ['daily_usage']
print(daily_sum.head())

# Fill days with 0 usage
idx = pd.date_range('2020-01-01', '2020-12-31')
daily_sum.index = pd.DatetimeIndex(daily_sum.index)
df_filled = daily_sum.reindex(idx, fill_value=0) # Fill missing values


# Get day of the week, Monday=0, Sunday=6, and week days names
dataset['DoW'] = dataset.transaction_date.dt.dayofweek
# do the same in a different way
dataset['weekday'] = dataset.transaction_date.dt.weekday
# get day names
dataset['day_name'] = dataset.transaction_date.apply(lambda x: x.day_name())

マルチカテゴリ/名義

2つ以上のカテゴリがあり、関連する順序がない。各特徴のカテゴリ情報を取得するには、dataset.describe(include='all')を使用します。

  • 参照文字列例を識別する列です人の名前。これは重複する可能性があります同じ名前の2人がいるかもしれませんが、ほとんどはユニークです。このデータは無用であり、削除する必要があります
  • キーカラムテーブル間のデータをリンクするために使用されます。この場合、要素はユニークです。このデータは無用であり、削除する必要があります

マルチカテゴリの列を数値にエンコードするために(機械学習アルゴリズムが理解できるように)、ダミーエンコーディングが使用されます(そしてワンホットエンコーディングは使用されません、なぜなら完全多重共線性を回避しないからです)。

pd.get_dummies(dataset.column1)を使用して、マルチカテゴリの列をワンホットエンコードできます。これにより、すべてのクラスがバイナリ特徴に変換され、これにより可能なクラスごとに1つの新しい列が作成され、1つの列にTrue値が割り当てられ、残りはFalseになります。

pd.get_dummies(dataset.column1, drop_first=True)を使用して、マルチカテゴリの列をダミーエンコードできます。これにより、すべてのクラスがバイナリ特徴に変換され、これにより可能なクラスごとに1つの新しい列が作成され、最後の2つの列が最後に作成されたバイナリ列で「1」または「0」として反映されます。これにより、完全多重共線性が回避され、列間の関係が減少します。

共線性/多重共線性

共線性は2つの特徴が互いに関連しているときに発生します。多重共線性はそれが2つ以上の場合に発生します。

機械学習では、特徴が可能な結果と関連していることを望みますが、それらが互いに関連していることは望ましくありません。そのため、ダミーエンコーディングはその最後の2つの列を混合し、ワンホットエンコーディングよりも優れています。ワンホットエンコーディングは、マルチカテゴリの列からのすべての新しい特徴間に明確な関係を作成することはないためです。

VIFは分散膨張係数であり、特徴の多重共線性を測定します。値が5を超えると、2つ以上の共線性のある特徴の1つを削除する必要があります

from statsmodels.stats.outliers_influence import variance_inflation_factor
from statsmodels.tools.tools import add_constant

#dummies_encoded = pd.get_dummies(dataset.column1, drop_first=True)
onehot_encoded = pd.get_dummies(dataset.column1)
X = add_constant(onehot_encoded) # Add previously one-hot encoded data
print(pd.Series([variance_inflation_factor(X.values,i) for i in range(X.shape[1])], index=X.columns))

カテゴリ不均衡

これは、トレーニングデータにおいて各カテゴリの量が同じでない場合に発生します。

# Get statistic of the features
print(dataset.describe(include='all'))
# Get an overview of the features
print(dataset.info())
# Get imbalance information of the target column
print(dataset.target_column.value_counts())

不均衡の場合、常に多数クラスまたは少数クラスがあります。

この問題を解決するための主な方法は2つあります

  • アンダーサンプリング:多数クラスからランダムにデータを削除し、少数クラスと同じサンプル数にします。
from imblearn.under_sampling import RandomUnderSampler
rus = RandomUserSampler(random_state=1337)

X = dataset[['column1', 'column2', 'column3']].copy()
y = dataset.target_column

X_under, y_under = rus.fit_resample(X,y)
print(y_under.value_counts()) #Confirm data isn't imbalanced anymore
  • Oversampling: 少数クラスのデータを増やし、それが多数クラスと同じサンプル数になるまで。
from imblearn.under_sampling import RandomOverSampler
ros = RandomOverSampler(random_state=1337)

X = dataset[['column1', 'column2', 'column3']].copy()
y = dataset.target_column

X_over, y_over = ros.fit_resample(X,y)
print(y_over.value_counts()) #Confirm data isn't imbalanced anymore

sampling_strategy引数を使用して、アンダーサンプリングまたはオーバーサンプリングを行うパーセンテージを指定できます(**デフォルトでは1100%**で、これは少数クラスの数を多数クラスと同じにすることを意味します)

{% hint style="info" %} アンダーサンプリングまたはオーバーサンプリングは完璧ではありません。オーバー/アンダーサンプリングされたデータの統計(.describe()で)を取得し、元のデータと比較すると、変化していることがわかります。したがって、オーバーサンプリングとアンダーサンプリングはトレーニングデータを変更しています。 {% endhint %}

SMOTEオーバーサンプリング

SMOTEは通常、データをオーバーサンプリングするより信頼性の高い方法です。

from imblearn.over_sampling import SMOTE

# Form SMOTE the target_column need to be numeric, map it if necessary
smote = SMOTE(random_state=1337)
X_smote, y_smote = smote.fit_resample(dataset[['column1', 'column2', 'column3']], dataset.target_column)
dataset_smote = pd.DataFrame(X_smote, columns=['column1', 'column2', 'column3'])
dataset['target_column'] = y_smote
print(y_smote.value_counts()) #Confirm data isn't imbalanced anymore

まれに発生するカテゴリ

ターゲットクラスの1つが非常に少ない回数で発生するデータセットを想像してください。

これは前のセクションのカテゴリの不均衡と似ていますが、まれに発生するカテゴリはその場合の「少数派クラス」よりもさらに少なく発生しています。生の オーバーサンプリングアンダーサンプリング 方法もここで使用できますが、一般的にこれらのテクニックは実際には良い結果を得ることができません

重み

一部のアルゴリズムでは、モデルを生成する際に、対象データの重みを変更して、デフォルトでより重要度の高いデータをいくつか取得することが可能です。

weights = {0: 10 1:1} #Assign weight 10 to False and 1 to True
model = LogisticRegression(class_weight=weights)

主成分分析PCA - Principal Component Analysis

データの次元を削減するのに役立つ手法です。異なる特徴を組み合わせ、それらの量を減らしより有用な特徴を生成します(計算量が少なくて済む)。

結果として得られる特徴は人間には理解できないため、データを匿名化します。

不一致のラベルカテゴリ

データには、変換の失敗やデータの記入時のヒューマンエラーによる間違いがあるかもしれません。

そのため、スペルミスのある同じラベル大文字小文字の違い略語などが見つかるかもしれません。例: BLUE, Blue, b, bule。これらのラベルエラーを、モデルのトレーニングの前に修正する必要があります。

すべてのデータが正しくラベル付けされていることを確認することが非常に重要です。たとえば、データ内の1つのスペルミスがある場合、クラスをダミーエンコードすると、最終的な特徴に新しい列が生成され、最終モデルに悪影響を与える可能性があります。この例は、1つの列をワンホットエンコードして生成された列の名前を確認することで非常に簡単に検出できます。

欠損データ

研究の一部のデータが欠落している場合があります。

完全にランダムなデータがエラーのために欠落している可能性があります。これは完全にランダムに欠落しているデータMCAR)です。

ランダムなデータが欠落している可能性があり、特定の詳細が欠落する可能性が高い要因があるかもしれません。たとえば、男性は年齢を教えることが多いが、女性は教えないことがよくあります。これはランダムに欠落しているデータMAR)と呼ばれます。

最後に、データがランダムに欠落していないMNAR)可能性があります。データの値はデータの存在確率と直接関係しています。たとえば、恥ずかしいことを測定したい場合、誰かが恥ずかしいほど、それを共有する可能性は低くなります。

最初の2つのカテゴリの欠損データは無視できるかもしれません。しかし、3番目のものは、影響を受けていないデータのみを考慮するか、欠損データをどうにかモデル化する必要があります。

欠損データについて知る方法の1つは、.info() 関数を使用することです。これにより、行数だけでなく、カテゴリごとの値の数が示されます。あるカテゴリに値の数が行数よりも少ない場合、データが欠落している可能性があります。

# Get info of the dataset
dataset.info()

# Drop all rows where some value is missing
dataset.dropna(how='any', axis=0).info()

通常、データセットの20%以上で特徴が欠落している場合、その列は削除されるべきです:

# Remove column
dataset.drop('Column_name', axis='columns', inplace=True)
dataset.info()

{% hint style="info" %} データセットに欠損値がすべて欠損しているわけではないことに注意してください。欠損値は"Unknown"、"n/a"、""、-1、0などの値が与えられている可能性があります。データセットをチェックする必要がありますdataset.column_name.value_counts(dropna=False)を使用して可能な値を確認します)。 {% endhint %}

データセットにデータが欠落している場合(それほど多くない場合)、欠損データのカテゴリを見つける必要があります。基本的に、欠損データがランダムかどうかを知る必要があり、そのためには欠損データが他のデータと相関しているかどうかを見つける必要があります。

欠損値が他の列と相関しているかどうかを見つけるには、データが欠落しているかどうかに応じて1と0を入れる新しい列を作成し、それらの間の相関を計算することができます

# The closer it's to 1 or -1 the more correlated the data is
# Note that columns are always perfectly correlated with themselves.
dataset[['column_name', 'cloumn_missing_data']].corr()

欠損データを無視することを決定した場合、それに対処する必要があります: 欠損データを持つ行を削除します(モデルのトレーニングデータが小さくなります)、特徴を完全に削除するか、それをモデル化することができます。

欠損している特徴とターゲット列との相関関係をチェックして、その特徴がターゲットにとってどれだけ重要かを確認する必要があります。非常に小さい場合は、それを削除するか、埋めることができます。

欠損している連続データを埋めるためには、平均値、中央値、または代入アルゴリズムを使用できます。代入アルゴリズムは、他の特徴を使用して欠損している特徴の値を見つけようとします。

from sklearn.impute import KNNImputer

X = dataset[['column1', 'column2', 'column3']]
y = dataset.column_target

# Create the imputer that will fill the data
imputer = KNNImputer(n_neightbors=2, weights='uniform')
X_imp = imputer.fit_transform(X)

# Check new data
dataset_imp = pd.DataFrame(X_imp)
dataset.columns = ['column1', 'column2', 'column3']
dataset.iloc[10:20] # Get some indexes that contained empty data before

カテゴリカルデータを埋めるには、まず、値が欠落している理由があるかどうかを考える必要があります。ユーザーの選択によるものであれば(データを提供したくなかった場合)、新しいカテゴリを作成して示すことができます。人為的なエラーによるものであれば、行または特徴量を削除するか(前述の手順を確認してください)、または最頻値で埋めることができます。

特徴量の結合

通常、相関関係がある2つの特徴量が見つかった場合、通常はそれらのうちの1つを削除するべきですターゲットとの相関が低い方を。ただし、それらを組み合わせて新しい特徴量を作成することもできます。

# Create a new feautr combining feature1 and feature2
dataset['new_feature'] = dataset.column1/dataset.column2

# Check correlation with target column
dataset[['new_feature', 'column1', 'column2', 'target']].corr()['target'][:]

# Check for collinearity of the 2 features and the new one
X = add_constant(dataset[['column1', 'column2', 'target']])
# Calculate VIF
pd.Series([variance_inflation_factor(X.values, i) for i in range(X.shape[1])], index=X.columns)
htARTEHackTricks AWS Red Team Expert を通じてゼロからヒーローまでAWSハッキングを学ぶ

HackTricksをサポートする他の方法