Pythonアルゴリズムは、不正防止ケースの完全なモデリングプロセスを実現します!非常に多くの通信詐欺?

近年、国内の通信詐欺事件はますます激しくなっています。この記事では、データ処理から、ランダムフォレストアルゴリズムを使用して、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'、 'C​​CUST_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'、 'C​​UST_TELE_CNT'、 'C​​UST_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'、 'C​​OUPLE_NUMBER'、 'TIME_COUPLE_NUMBER'、 'ZJ_0912'、 'HB_0912'、 'ZJ_1417'、 'HB_1417'、 'C​​HG_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'、 'C​​ORR_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の学習問題について相談しました。コミュニケーションを促進するには、青をクリックしてディスカッションに参加し、自分でリソースベースに回答してください

 

おすすめ

転載: blog.csdn.net/weixin_43881394/article/details/113047901