近年、国内の通信詐欺事件はますます激しくなっています。この記事では、データ処理から、ランダムフォレストアルゴリズムを使用して、Python機械学習ツールを使用して、州の通信会社の不正防止モデルの簡略版を事例として使用します。 、機能エンジニアリング、不正防止モデルへモデルの構築と評価の完全なプロセスの簡単な記録と紹介。
フローチャート
環境設定、モジュールの読み込み
#コーディング:utf-8 import os import numpy as np import pandas as pd from sklearn.ensemble import IsolationForest from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import confusion_matrix from sklearn.externals import joblib from sklearn from scipy import stats import time from datetime import datetime import warnings warnings.filterwarnings( "ignore") os.chdir( 'home // zj // python // python3.6.9 // bin // python3') 123456789101112131415161718
データの読み込み
作業ディレクトリをカスタマイズし、サンプルデータをロードします
def read_file(filepath): os.chdir(os.path.dirname(filepath)) return pd.read_csv(os.path.basename(filepath)、encoding = 'utf-8') file_pos = "E:\\ work file \\ *** \\不正防止の識別\\ data_train.csv " data_pos = read_file(file_pos) 123456
機能の名前変更
data_pos.columns = ['BIL_ACCS_NBR'、 'ASSET_ROW_ID'、 'CCUST_ROW_ID'、 'LATN_ID'、 'TOTAL_CNT'、 'TOTAL_DURATION'、 'ZJ_CNT'、 'ZJ_TOTAL_DURATION'、 'TOTAL_ROAM_CNT'、 'ZJ_ROAM_CNT'、 ZJ_ROAM_DURATION '、' ZJ_LOCAL_DURATION '、' ZJ_LONG_CNT '、' BJ_LOCAL_CNT '、' WORK_TIME_TH_TT_CNT '、' FREE_TIME_TH_TT_CNT '、' NIGHT_TIME_TH_TT_CNT '、' DURATION_TP_1 '、' DURATION_TP_2 '、' DURATION_TP_1 '、' DURATION_TP_2 '、' DURATION_ 、 'DURATION_TP_7'、 'DURATION_TP_8'、 'DURATION_TP_9'、 'TOTAL_DIS_BJ_NUM'、 'DIS_BJ_NUM'、 'DIS_OPP_HOME_NUM'、 'OPP_HOME_NUM'、 'MSC_NUM'、 'DIS_MSC_NUM'、 'ZJ_AVG_DURATION '、' TOTAL_ROAM_CNT_RATE '、' ZJ_DURATION_RATE '、' ZJ_CNT_RATE '、' ZJ_ROAM_DURATION_RATE '、' ZJ_ROAM_CNT_RATE '、' DURATION_RATIO_0_15 '、' DURATION_RATIO_15_30 '、ZJ_AVG_DURATION '、' TOTAL_ROAM_CNT_RATE '、' ZJ_DURATION_RATE '、' ZJ_CNT_RATE '、' ZJ_ROAM_DURATION_RATE '、' ZJ_ROAM_CNT_RATE '、' DURATION_RATIO_0_15 '、' DURATION_RATIO_15_30 '、 ' DURATION_RATIO_15_30 '、' DURATION_R 'DUR_60_CNT_RATE'、 'DUR_90_CNT_RATE'、 'DUR_120_CNT_RATE'、 'DUR_180_CNT_RATE'、 'DUR_BIGGER_180_CNT_RATE'、 'DIS_BJ_NUM_RATE'、 'TOTAL_DIS_BJ_NUM_RATE'、 'CALLING_REGION_DISTRI_LEVEL'、 'ACT_DAY'、 'ACT_DAY_RATE'、 'WEEK_DIS_BJ_NUM'、 'YY_WORK_DAY_OIDD_23_NUM'、 'IS_GJMY '、' ZJ_DURATION_0_15_CNT '、' ZJ_DURATION_15_30_CNT '、' ZJ_DURATION_30_60_CNT '、' ZJ_DURATION_RATIO_0_15 '、' ZJ_DURATION_RATIO_15_30 '、' NAME'TH'RC'TH 'RD、 'CUST_ASSET_CNT'、 'CUST_TELE_CNT'、 'CUST_C_CNT'、 'ALL_LL_USE'、 'MY_LL_USE'、 'MY_LL_ZB'、 'ALL_LL_DUR'、 'MY_LL_DUR '、' MY_DUR_ZB '、' AGE '、' GENDER '、' CUST_TYPE_GRADE_NAME '、' ISP '、' TERM_PRICE '、' SALES_CHANNEL_LVL2_NAME '、' CORP_USER_NAME '、' TOTOL_7_CNT '、 ' TOTOL_'TOTOL_7_CNT '、' TOTOL_7_DUR_ '、' 、 'TOTOL_7_ZJ_D_CNT'、 'TOTOL_7_BJ_D_DUR'、 'TOTOL_7_JZGS_CNT'、 'WEEK_CNT'、 'WEEK_DUR'、 'ZB_WS'、 'COUPLE_NUMBER'、 'TIME_COUPLE_NUMBER'、 'ZJ_0912'、 'HB_0912'、 'ZJ_1417'、 'HB_1417'、 'CHG_CELLS'、 'ZHANBI'、 'ET '、' IS_HARASS '] 12345678
データビュー
データテーブルの行/列
data_pos.shape 1
正のサンプルデータはわずか3436であり、非常に不均衡なサンプルデータである多くの負のサンプルがあることがわかります。
データの前処理
意味のないフィールドの削除
data_pos_1 = data_pos.drop([ 'BIL_ACCS_NBR'、 'ASSET_ROW_ID'、 'CCUST_ROW_ID'、 'LATN_ID'、 'CPRD_NAME'、 'ISP'、 'AGE'、 'CUST_TYPE_GRADE_NAME'、 'ETL_DT'、 'WEEK_DIS_BJ_NUM_'、 ' 、 'TOTOL_7_JZGS_CNT'、 'INNER_MONTH' ]、axis = 1) 123456789101112131415
正と負のサンプルサイズ
data_pos.IS_HARASS.value_counts() 1
ビニング処理用のTERM_PRICE
data_pos_1 ['TERM_PRICE'] = data_pos_1 ['TERM_PRICE']。apply(lambda x:np.where(x> 5000、 '> 5000'、 np.where(x> 3000、 '(3000,5000]'、 np。 where(x> 2000、 '(2000,3000]'、 np.where(x> 1000、 '(1000,2000]'、 np.where(x> 0、 '(0,1000]'、 '未识加' )))))) 123456
フィールドの入力と変換
カテゴリ変数と非常に小規模なカテゴリのnull値を置き換えます
data_pos_1.TERM_PRICE.value_counts() data_pos_1.MIX_CDSC_FLG.value_counts() data_pos_1.CORP_USER_NAME.value_counts() data_pos_1.SALES_CHANNEL_LVL2_NAME.value_counts() 1234
#Process TERM_PRICE、MIX_CDSC_FLG、CORP_USER_NAME、SALES_CHANNEL_LVL2_NAME def CHANGE_SALES_CHANNEL_LVL2_NAME(data): ['ソーシャルチャネル'、 '物理チャネル'、 '電子チャネル'、 '直接販売チャネル']の データの場合 :データを返すelse: ' 不明な'データを返す['SALES_CHANNEL_LVL2_NAME'] = data_pos_1.SALES_CHANNEL_LVL2_NAME.apply(CHANGE_SALES_CHANNEL_LVL2_NAME) 12345678
欠測値の処理
##缺失值を计 defna_count(data): data_count = data.count() na_count = len(data)-data_count na_rate = na_count / len(data) na_result = pd.concat([data_count、na_count、na_rate]、axis = 1) na_resultを返す na_count = na_count(data_pos_1) na_count 12345678910
分割フィールド
フィールドは、連続およびカテゴリに従って分割されます
def category_continuous_resolution(data、variable_category): for key in list(data.columns): if key not in variable_category: variable_continuous.append(key) else: continue return variable_continuous #字段按照クラス型拆分 variable_category = ['MIX_CDSC_FLG'、 'GENDER '、' TERM_PRICE '、' SALES_CHANNEL_LVL2_NAME '、' CORP_USER_NAME '] variable_continuous = [] variable_continuous = category_continuous_resolution(data_pos_1、variable_category) 1234567891011121314
フィールドタイプ変換
def feture_type_change(data、variable_category): '' ' 字段クラス型変換 ' '' for col_key in list(data.columns): if col_key in variable_category: data [col_key] = data [col_key] .astype(eval( 'object') 、copy = False) else: data [col_key] = data [col_key] .astype(eval( 'float')、copy = False) return data data_pos_2 = feture_type_change(data_pos_1、variable_category) 123456789101112
欠測値の入力
def na_fill(data、col_name_1、col_name_2): '' ' 缺失值表充 ' '' for col_key in list(data.columns): if col_key in col_name_1: data [col_key] = data [col_key] .fillna(value = '裏识 ') col_name_2のelif col_key: data [col_key] = data [col_key] .fillna(data [col_key] .mean()) else: data [col_key] = data [col_key] .fillna(value = 0) return data #缺失值可能充 col_name_1 = variable_category col_name_2 = [] data_pos_3 = na_fill(data_pos_2、col_name_1、col_name_2) 1234567891011121314151617
カテゴリ変数one_hot処理
## one_hot def data_deliver(data、variable_category): '' ' ont_hot衍生 ' '' for col_key in list(data.columns): if col_key in variable_category: temp_one_hot_code = pd.get_dummies(data [col_key]、prefix = col_key) data = pd.concat([data、temp_one_hot_code]、axis = 1) del data [col_key] else: 続行 データを 返すdata_pos_4 = data_deliver(data_pos_3、variable_category) 123456789101112131415
機能工学
相関分析
def max_corr_feture_droped(train_data、variable_continuous、k): '' '関連関係 分析 ' '' table_col = train_data.columns table_col_list = table_col.values.tolist() all_lines = len(train_data) train_data_number = train_data [variable_continuous] ###续変化量的理過程:数データ的データ電子化 from numpy import array from sklearn import preprocessing def normalization(data、method、feature_range =(0,1)): if method == 'MaxMin': train_data_scale = data.apply(lambda x :( x-np.min(x))/(np.max(x)-np.min(x))) return train_data_scale if method == 'z_score': train_data_scale = data.apply(lambda x:(x --np.mean(x))/(np.std(x))) return train_data_scale train_data_scale = normalization(train_data_number、method = scale_method) #全出4报告 defdata_corr_analysis(raw_data、sigmod = k): corr_data = raw_data.corr() for i in range(len(corr_data)): for j in range(len(corr_data)): if j == i: corr_data.iloc [ i、j] = 0 x、y、corr_xishu = []、[]、[] for i in list(corr_data.index): for j in list(corr_data.columns): if abs(corr_data.loc [i、j]) > sigmod:#相関係数の絶対値がしきい値より大きい属性を保持します x.append(i) y.append( j) corr_xishu.append(corr_data.loc [i、j]) z = [[x [i]、y [i]、corr_xishu [i]] for i in range(len(x))] high_corr = pd.DataFrame(z 、columns = ['VAR1'、 'VAR2'、 'CORR_XISHU']) return high_corr high_corr_data = data_corr_analysis(train_data_number、sigmod = k) def data_corr_choice(data、train_data_scale、high_corr_data): high_corr_data_1 = [] target_var .loc [:、target_col]) for i in range(high_corr_data.shape [0]): for j in range(high_corr_data.shape [1] -1): d1 = pd.DataFrame(train_data_scale。 loc [:、high_corr_data.iloc [i、j]]) data1 = pd.concat([d1、data.loc [: 、target_col]]、axis = 1、join = 'inner') corr_data = data1.corr() high_corr_data_1.append(corr_data.iloc [0、-1])#未出的是4変量与ハイ _corr_data_2 = np.array(high_corr_data_1).reshape(high_corr_data.shape [0]、high_corr_data.shape [1] -1) high_corr_data_2 = pd.DataFrame(high_corr_data_2、columns = high_corr_data.columns [:- 1]) del_var_cor = [] for i in range(high_corr_data_2.shape [0]): if abs(high_corr_data_2.iloc [i、0])> = abs(high_corr_data_2.iloc [i、1]): del_var_cor.append( high_corr_data.iloc [i、1]) else: del_var_cor.append(high_corr_data.iloc [i、0]) train_data_number_2.drop(del_var_cor、axis = 1、inplace = True)#強く相関します変数は直接削除されます returnset(high_corr_data_1)、set(del_var_cor)、train_data_number_2 train_data_number_2 = pd.concat([train_data [variable_continuous]、train_data [target_col]、軸= 1) high_corr_data_1、del_var_cor、train_data_scale = data_corr_choice(train_data_number_2、train_data_scale、high_corr_data) train_data2 = train_data [:] train_data2.drop(セット(del_var_cor) 、axis = 1、 inplace = True)return train_data2、del_var_cor #相関連性分析、去除高 相関連量scale_method = 'MaxMin' target_col = 'IS_HARASS' data_pos_5、del_var_cor = max_corr_feture_droped(data_pos_4、variable_continuous、k = 0.8) del_var_cor#変数ビューを削除 123456789101112
特徴重要性分析
DEF DATA_SAMPLE(データ、target_col、SMP): '' ' 数据平衡 ''' DATA_1 =データ[[target_col] == 1] .sample(FRAC = 1) DATA_0 =データ[[target_col] == 0]。 sample(n = len(data_1)* smp) #data_1 = data_1.sample(len(data_2)* smp) data = pd.concat([data_1、data_0])。reset_index() return data 123456789
def train_test_spl(data): '' ' トレーニングデータ、テストデータセグメンテーション ' '' X_train、X_test、y_train、y_test = train_test_split( data [ipt_col]、data [target_col]、test_size = 0.2、random_state = 42) return X_train、X_test 、y_train、y_test 1234567
特徴重要度分析関数を定義し、それをループして最適なサンプリング比を取得します
def feture_extracted(train_data、alpha): '' ' 次元の重要性の判断 ' '' global ipt_col ipt_col = list(train_data.columns) ipt_col.remove(target_col) sample_present = [1,5]#サンプリング比を定義します f1_score_list = [] model_dict = {} for i in sample_present: try: train_data = data_sample(train_data、target_col、smp = i) except ValueError: break X_train、X_test、y_train、y_test = train_test_spl(train_data)#RF 選択機能 モデルの開始= RandomForestClassifier () モデル= model.fit(X_train、y_train) model_pred = model.predict(X_test) f1_score = metrics.f1_score(y_test、model_pred) f1_score_list.append(f1_score) model_dict [i] = model max_f1_index = f1_score_list.index(max(f1_score_list)) print( '最にする上の例申し是:1:'、sample_present [max_f1_index]) d = dict(zip(ipt_col、[float( '%。3f'%i)for i in model_dict [sample_present [max_f1_index]]。feature_importances _])) f = zip(d.values()、d.keys()) importance_df = pd.DataFrame(sorted(f、reverse = True)、columns = ['importance'、 'feture_name']) list_imp = np.cumsum(importance_df ['importance'])。tolist() for i、j in enumerate (list_imp): if j> = alpha: break print( '大表alpha的特攻勝性如下:\ n'、importance_df。iloc [0:i + 1 、:]) print( '特性は次のとおりです:') feture_selected = importance_df.iloc [0:i + 1、1] .tolist() print(feture_selected) feture_selected #importance testを返し、重要な変数を選択します data_pos_5_feture = feture_extracted(data_pos_5、alpha = 0.9) 12345678910111213141516171819202122232425262728293031323334353637383940
モデルトレーニング
データバランス
data_pos_6 = data_sample(data_pos_5、target_col、smp = 3) 1
正と負のサンプル分割
def model_select(data、rf_feture、target_col、test_size): '' ' 正负例本拆分 ' '' X_train、X_test、y_train、y_test = train_test_split( data [rf_feture]、data [target_col]、test_size = test_size、random_state = 42 ) return X_train、X_test、y_train、y_test #拆分孔7:3 X_train、X_test、y_train、y_test = model_select(data_pos_6、data_pos_5_feture、target_col、test_size = 0.3) 12345678910
モデル関数を定義する
RFの2つの主要なパラメータ:
- min_samples_split:内部ノードを分割する場合、ノード上のサンプルの最小数が必要です。デフォルトは2です。
- min_samples_leaf:リーフノードのサンプルの最小数を設定します。デフォルトは1です。ノードを分割しようとすると、分割後の左右の分岐のサンプル数がこのパラメータで指定された値以上の場合にのみ、ノードが分割されたと見なされます。つまり、サンプル数の場合リーフノードの値が値よりも小さいパラメータで値が指定されている場合、リーフノードとその兄弟ノードが削除されます。サンプルデータの量が多い場合は、この値を増やしてツリーの成長を早期に終了することを検討できます。
def model_train(x_train、y_train、model): '' ' 算法絵、默认是RF ' '' if model == 'RF': res_model = RandomForestClassifier(min_samples_split = 50、min_samples_leaf = 50) res_model = res_model.fit(x_train、 y_train) feature_importances = res_model.feature_importances_ [1] if model == 'LR': res_model = LogisticRegression() res_model = res_model.fit(x_train、y_train) list_feature_importances = [x for x in res_model.coef_ [0]] list_index = list (x_train.columns) feature_importances = pd.DataFrame(list_feature_importances、list_index) else: pass return res_model、feature_importances #Training モデルrf_model、feature_importances = model_train(X_train、y_train、モデル= 'RF')#YouもLR使用するように選択することができ 123456789101112131415161718192021を
モデルの検証
def model_predict(res_model、input_data、alpha): #モデル予測#input_data :ターゲット変数なしで新しいデータを入力 data_proba = pd.DataFrame(res_model.predict_proba(input_data).round(4)) data_proba.columns = ['neg'、 ' POS '] data_proba [' RES '] = data_proba [' POS']適用する。(ラムダX:np.where(X> =アルファ、1、0))#Adjust> 0.5の出力として正1つのに 戻りdata_proba DEF model_evaluate(y_true、y_pred): y_true = np.array(y_true) y_true.shape =(len(y_true)、) y_pred = np.array(y_pred) y_pred.shape =(len(y_pred)、) print(metrics。classification_report (y_true、y_pred)) 1234567891011121314
data_pos_6 = data_sample(data_pos_5、target_col、smp = 50) X_train、X_test、y_train、y_test = model_select(data_pos_6、data_pos_5_feture、target_col、test_size = 0.5)適合率 = [] 再現 率= [] np.arange(0、1 、0.1): y_pred_rf = model_predict(rf_model、X_test、alpha = alpha) cnf_matrix = confusion_matrix(y_test、y_pred_rf ['res']) Precision.append((cnf_matrix [1,1] /(cnf_matrix [0,1] + cnf_matrix [1,1]))。round(4)) Recall.append((cnf_matrix [1,1] /(cnf_matrix [1,0] + cnf_matrix [1,1]))。round(4)) スコア= pd .DataFrame(np.arange(0、1、0.1)、columns = ['score']) Precision = pd.DataFrame(Precision、columns = ['Precision']) Recall = pd.DataFrame(Recall、columns = ['想起']) Precision_Recall_F1 = pd.concat([score、Precision、Recall]、axis = 1) Precision_Recall_F1 ['F1'] =(2 * Precision_Recall_F1 ['Precision'] * Precision_Recall_F1 ['Recall'] /(Precision_Recall_F1 ['Precision'] + Precision_Recall_F1 ['Recall']))。round(2) Precision_Recall_F1 123456789101112415
モデルパッケージの保存
start = datetime.now() joblib.dump(rf_model、 'model.dmp'、compress = 3) print( "モデルの保存にかかった時間:%s秒"%(datetime.now()-start).seconds ) 123
上記のケースは比較的単純で、RFアルゴリズムを含む多くのデータのクリーニングと前処理を必要とせず、2つのパラメーターのみを定義し、パラメーターの最適化プロセスはありません。興味のある人は、これに基づいてさらに深く掘り下げることができます。
最近、多くの友人がプライベートメッセージを通じてPythonの学習問題について相談しました。コミュニケーションを促進するには、青をクリックしてディスカッションに参加し、自分でリソースベースに回答してください