Окончательный вариант работы по финансовой разведке

искусственный интеллект

[toc]

Источник набора данных

2021 Китайский университет Студенческое страхование Digital Challenge-Digital Track(начиная с 26 апреля 2021 г.)

2021 China University Student Insurance Digital Challenge» спонсируется организационным комитетом конкурса, состоящим из единственной ежедневной рабочей группы, отвечающей за Комиссию по регулированию банковской и страховой деятельности Китая «Новости банковского дела и страхования Китая», Страхового общества Китая и China Ping An Property. Страхование, Шэньчжэнь Исследования больших данных Совместно организованный колледжем, Zhihu представляет собой комплексный проект мероприятий кампуса в качестве платформы для сотрудничества по содержанию.

Описание проблемы

На основе платформы анализа данных и моделирования, предоставленной официальным мероприятием, участникам необходимо в полной мере использовать информацию, содержащуюся в данных, и объединить бизнес-характеристики страхования, не связанного с автомобилем, для проведения обоснованной предварительной обработки данных и полной обработки и анализа данных для прогнозирования. каждое целевое (каридное) намерение приобрести товары, не связанные с автомобилем. (Финальный конкурс озаглавлен «Вопросы, связанные со страховым бизнесом», и его сложность увеличится. Конкретные подробности конкурса будут объявлены после завершения полуфинала.)

Результаты матча

После недели напряженной работы лучший результат — auc=0,90001787, рейтинг: (20/314).image.png

предварительная обработка данных

предварительные данные

  • Чтение обучающих и тестовых данных
df_train = pd.read_csv('/home/mw/input/pre8881/train.csv',index_col=0)
df_test = pd.read_csv('/home/mw/input/pretest_a3048/test_a.csv',index_col=0)
df_train.shape,df_test.shape
((684283, 65), (186925, 64))

Среди них 684 283 обучающих набора и 186 925 тестовых наборов. Содержит в общей сложности 65 измерений данных ylabel. Конкурсные вопросы не дают конкретных значений полей, что также усложняет понимание конкурсных вопросов.

Index(['dpt', 'xz', 'xb', 'carid', 'nprem_ly', 'ncd_ly', 'newvalue',
       'bi_renewal_year', 'clmnum', 'regdate', 'trademark_cn', 'brand_cn',
       'make_cn', 'series', 'capab', 'seats', 'use_type', 'change_owner',
       'nprem_od', 'si_od', 'nprem_tp', 'si_tp', 'nprem_bt', 'si_bt',
       'nprem_vld', 'si_vld', 'nprem_vlp', 'si_vlp', 'p1_prior_days_to_insure',
       'suiche_nonauto_nprem_20', 'suiche_nonauto_nprem_19',
       'suiche_nonauto_nprem_18', 'suiche_nonauto_nprem_17',
       'suiche_nonauto_nprem_16', 'suiche_nonauto_amount_20',
       'suiche_nonauto_amount_19', 'suiche_nonauto_amount_18',
       'suiche_nonauto_amount_17', 'suiche_nonauto_amount_16',
       'num_notcar_claim', 'p1_gender', 'p1_age', 'p1_census_register',
       'p2_marital_status', 'f1_child_flag', 'f2_posses_house_flag',
       'f2_cust_housing_price_total', 'p2_client_grade', 'w1_pc_wx_use_flag',
       'p1_is_bank_eff', 'p2_is_enterprise_owner', 'p2_is_smeowner',
       'active_7', 'active_30', 'active_90', 'active_365',
       'p2_is_child_under_15_family', 'p2_is_adult_over_55_family',
       'birth_month', 'p1_service_offer_cnt', 'p3_service_use_cnt',
       'dur_personal_insurance_90', 'service_score_available',
       'y1_is_purchase'],
      dtype='object')
  dtypes: float64(32), int64(9), object(23)
      

Из 65 полей 32 имеют тип float64, 9 — int64 и 23 — объектного типа.

Отсутствует заполнение значения

  • Просмотрите 20 полей с наибольшим количеством отсутствующих значений.
df_train.isnull().sum().sort_values(ascending=False)[:20]

image.png

Если поле имеет числовой тип, отсутствующее значение будет заполнено режимом, а номинальный атрибут пока не будет заполнен.Использование lightgbm классифицирует нулевое значение номинального атрибута в одну категорию.

columns = df_train.columns
for i in tqdm (range(len(columns))):
    # print(df_train[columns[i]].dtype)
    if  df_train[columns[i]].dtype!='object' and columns[i]!='y1_is_purchase' :
        # print(columns[i])
        df_train[columns[i]].fillna(df_train[columns[i]].mode()[0], inplace=True)
        df_test[columns[i]].fillna(df_test[columns[i]].mode()[0], inplace=True)
        
    
    

Кодировка номинального атрибута

LabelEncoder используется для кодирования категориальных собственных значений, то есть для кодирования прерывистых значений или текста

# 其他类别变量,直接类别编码
no_features = ['client_no', 'carid','y1_is_purchase']
data = pd.concat([df_train, df_test], axis=0)
for col in df_train.select_dtypes(include=['object']).columns:
    if col not in no_features:
        lb = LabelEncoder()
        lb.fit(data[col].astype(str))
        df_train[col] = lb.transform(df_train[col].astype(str))
        df_test[col] = lb.transform(df_test[col].astype(str))
features = [col for col in df_train.columns if col not in no_features]
features

Обработка данных даты

Сначала преобразуйте regdate в тип datetime, а затем получите данные года.

df_train['regdate']  = pd.to_datetime(df_train['regdate'])
df_test['regdate']  = pd.to_datetime(df_test['regdate'])
df_train['regdate']  = df_train['regdate'].dt.year
df_test['regdate']  = df_test['regdate'].dt.year

добавить свойства

Подсчитайте общее количество значений атрибутов поля ['dpt'], ['client_no'], ['trademark_cn'], ['brand_cn'], ['make_cn']

# 计数
for f in [['dpt'], ['client_no'], ['trademark_cn'], ['brand_cn'], ['make_cn'], ['series']]:
    df_temp = df_feature.groupby(f).size().reset_index()
    df_temp.columns = f + ['{}_count'.format('_'.join(f))]
    df_feature = df_feature.merge(df_temp, how='left')

Подсчитайте среднюю стоимость покупок для разных атрибутов поля ['p1_census_register', 'dpt'] в обучающей выборке.

# 简单统计
def stat(df, df_merge, group_by, agg):
    group = df.groupby(group_by).agg(agg)

    columns = []
    for on, methods in agg.items():
        for method in methods:
            columns.append('{}_{}_{}'.format('_'.join(group_by), on, method))
    group.columns = columns
    group.reset_index(inplace=True)
    df_merge = df_merge.merge(group, on=group_by, how='left')

    del (group)
    gc.collect()

    return df_merge


def statis_feat(df_know, df_unknow):
    for f in tqdm(['p1_census_register', 'dpt']):
        df_unknow = stat(df_know, df_unknow, [f], {
                         'y1_is_purchase': ['mean']})

    return df_unknow

анализ данных

Анализ равновесия образца

fig = plt.figure()
plt.pie(df_train['y1_is_purchase'].value_counts(),
        labels=df_train['y1_is_purchase'].value_counts().index,
        autopct='%1.2f%%',counterclock = False)
plt.title('购买率')
plt.show()
# flag_0 = df_train['y1_is_purchase']==0
# n = sum(flag_0)
# flag_1 = df_train['y1_is_purchase']==1
# n,sum(flag_1)

# df_train[flag_0].shape
# df_train = pd.concat([df_train[flag_0][:n], df_train[flag_1][:n*1.5]])
# # df_train = df_train.reset_index()
# df_train['y1_is_purchase'].value_counts()

image.png

Очевидно, сбалансированы данные или нет, я сразу подумал о даунсэмплинге и оверсемплинге, но эффект не так хорош, как исходные данные.

анализ распределения данных

Проверьте распределение числовых полей с помощью диаграмм и удалите данные, которые значительно отклоняются.


columns = df_train.columns.tolist()
fig = plt.figure(figsize=(80,60),dpi=75)
j = 0
for i in range(len(columns)):
    if not df_train[columns[i]].dtype=='object':
        j+=1
        plt.subplot(7,10,j+1)
        sns.boxplot(df_train[columns[i]],orient='v',width=0.5)
        plt.ylabel(columns[i],fontsize=36)
plt.show()

image.png

train_rows = len(df_train.columns)
plt.figure(figsize=(10*4,10*4))


columns = df_train.columns.tolist()
fig = plt.figure(figsize=(80,60),dpi=75)
j = 0
for i in range(len(columns)):
    # print(df_train[columns[i]].dtype)
    if  df_train[columns[i]].dtype=='int64' and columns[i]!='y1_is_purchase':
        # print(df_train[columns[i]].dtype)
        df_train[columns[i]].fillna(df_train[columns[i]].mode(), inplace=True)
        df_test[columns[i]].fillna(df_test[columns[i]].mode(), inplace=True)
        j+=1
         
    
        ax = plt.subplot(4,4,j)
        sns.distplot(df_train[columns[i]],fit=stats.norm)

        j+=1
        ax = plt.subplot(4,4,j)
        stats.probplot(df_train[columns[i]],plot=plt)
plt.tight_layout()
plt.show()

image.png

Применение модели

Древо решений

Дерево решений — это базовый метод классификации и регрессии. Модель дерева решений имеет высокую скорость классификации и легкую визуальную интерпретацию модели, но в то же время она также склонна к переоснащению, хотя и есть обрезка, но она неудовлетворительна.

В задаче классификации метод бустинга изучает несколько классификаторов, изменяя вес обучающих выборок (увеличивая вес неправильно классифицированных выборок и уменьшая вес отсоединенных выборок) и линейно комбинируя эти классификаторы для улучшения классификации. . Ускоряющее математическое представление:

f(x)=w0+m=1Mwmфm(x)f(x) = w_0 + \sum\limits_{m=1}^M w_m \phi_m(x)

где w - вес,ф\phiпредставляет собой набор слабых классификаторов, и можно видеть, что конечный результат представляет собой линейную комбинацию базисных функций.

Следовательно, комбинация дерева решений и бустинга дает множество алгоритмов, в основном включающих бустинг-дерево, GBDT и т. д.

Повышение градиента — это метод повышения, основная идея которого заключается в том, что каждый раз при построении модели направление градиентного спуска функции потерь модели устанавливается заранее. Функция потерь предназначена для оценки производительности модели (как правило, степень соответствия + регулярный член), Считается, что чем меньше функция потерь, тем лучше производительность. И пусть функция потерь продолжает уменьшаться, чтобы модель можно было постоянно изменять для повышения производительности.Лучший способ - заставить функцию потерь уменьшаться в направлении градиента (разумно, направление градиента уменьшается быстрее всего).

lightgbm

Gradient Boost — это фреймворк, в который можно встроить множество различных алгоритмов.

  • Алгоритм дерева решений на основе гистограммы.

Основная идея алгоритма гистограммы состоит в том, чтобы сначала разбить непрерывные собственные значения с плавающей запятой на k целых чисел, и в то же время построить гистограмму шириной k. При обходе данных статистика накапливается в гистограмме в соответствии с дискретизированным значением в виде индекса.После однократного обхода данных гистограмма накапливает требуемую статистику, а затем в соответствии с дискретным значением гистограммы она проходит, чтобы найти оптимальная точка сегментации.

  • Односторонняя выборка на основе градиента (GOSS): использование GOSS может уменьшить большое количество экземпляров данных только с небольшими градиентами, так что при расчете прироста информации можно использовать только оставшиеся данные с высокими градиентами.По сравнению с XGBoost, просматривающим все собственные значения, это экономит много времени и места накладных расходов.
  • Объединение эксклюзивных функций (EFB): с помощью EFB многие взаимоисключающие функции могут быть объединены в одну функцию, что позволяет достичь цели уменьшения размерности.

* Стратегия роста листьев по листам с ограничением по глубине: большинство инструментов GBDT используют неэффективную стратегию роста дерева решений по уровням, потому что они обрабатывают листья на одном уровне без разбора, что приводит к большому количеству ненужных накладных расходов. На самом деле коэффициент разделения многих листьев невелик, и нет необходимости искать и разделять. LightGBM использует листовой алгоритм с ограничениями по глубине.

  • Прямая поддержка категориальной функции
  • Поддерживает эффективный параллелизм
  • Оптимизация коэффициента попаданий в кэш
```python
ycol = 'y1_is_purchase'
feature_names = features
model = lgb.LGBMClassifier(objective='binary',
                           boosting_type='gbdt',
                           num_leaves=64,
                           max_depth=10,
                           learning_rate=0.01,
                           n_estimators=10000,
                           subsample=0.8,
                           feature_fraction=0.6,
                           reg_alpha=10,
                           reg_lambda=12,
                           random_state=seed,
                           is_unbalance=True,
                           metric='auc')

df_oof = df_train[['carid', ycol]].copy()
df_oof['prob'] = 0
prediction = df_test[['carid']]
prediction['prob'] = 0
df_importance_list = []

kfold = StratifiedKFold(n_splits=5, shuffle=True, random_state=seed)
for fold_id, (trn_idx, val_idx) in enumerate(
        kfold.split(df_train[feature_names], df_train[ycol])):
    X_train = df_train.iloc[trn_idx][feature_names]
    Y_train = df_train.iloc[trn_idx][ycol]

    X_val = df_train.iloc[val_idx][feature_names]
    Y_val = df_train.iloc[val_idx][ycol]

    print('\nFold_{} Training ================================\n'.format(
        fold_id + 1))
    # print(df_oof.loc[val_idx]['prob'])
    lgb_model = model.fit(X_train,
                          Y_train,
                          eval_names=['train', 'valid'],
                          eval_set=[(X_train, Y_train), (X_val, Y_val)],
                          verbose=100,
                          early_stopping_rounds=50)

    pred_val = lgb_model.predict_proba(
        X_val, num_iteration=lgb_model.best_iteration_)[:, 1]
    # print(df_oof.iloc[val_idx])
    df_oof.loc[val_idx, 'prob'] = pred_val

    pred_test = lgb_model.predict_proba(
        df_test[feature_names], num_iteration=lgb_model.best_iteration_)[:, 1]
    prediction['prob'] += pred_test / kfold.n_splits


    df_importance = pd.DataFrame({
        'column': feature_names,
        'importance': lgb_model.feature_importances_,
    })
    df_importance_list.append(df_importance)

    del lgb_model, pred_val, pred_test, X_train, Y_train, X_val, Y_val
    gc.collect()

Оценка модели

Пятикратная перекрестная проверка

Байесовская настройка

Байесовская оптимизация для настройки параметров машинного обучения была предложена Дж. Снуком (2012), основная идея заключается в том, что задана целевая функция оптимизации (обобщенная функция, достаточно указать вход и выход, не нужно знать внутреннюю структуру и математические свойства) , путем непрерывного добавления точек выборки для обновления апостериорного распределения целевой функции (процесс Гаусса) до тех пор, пока апостериорное распределение в основном не будет соответствовать реальному распределению. Проще говоря, информация о последнем параметре учитывается, чтобы лучше настроить текущие параметры.

Разница между ним и обычным поиском по сетке или случайным поиском заключается в следующем:

  • Байесовская настройка параметров использует гауссовский процесс, учитывая предыдущую информацию о параметрах и постоянно обновляя предшествующую; поиск по сетке не учитывает предыдущую информацию о параметрах.
  • Байесовская настройка параметров имеет мало итераций и высокую скорость; скорость поиска по сетке низкая, и при наличии большого количества параметров легко вызвать взрыв размерности.
  • Байесовская настройка параметров по-прежнему надежна для невыпуклых задач; поиск по сетке позволяет легко получить локальный оптимум для невыпуклых задач.
параметры управления обучением значение использование
max_depth максимальная глубина дерева Когда модель переоснащается, рассмотрите возможность сначала уменьшитьmax_depth
min_data_in_leaf Минимальное количество записей, которое может иметь лист По умолчанию 20, используется при переоснащении
feature_fraction Например, когда он равен 0,8, это означает, что 80% параметров выбираются случайным образом для построения дерева на каждой итерации. Используется при прокачке случайного леса
bagging_fraction Масштаб данных, используемых на каждой итерации Используется для ускорения обучения и уменьшения переобучения
early_stopping_round Если одна мера проверочных данных находится в самой последнейearly_stopping_roundНет улучшения в раунде, модель перестанет тренироваться Ускорьте анализ и сократите количество чрезмерных итераций
lambda Укажите регуляризацию 0~1
min_gain_to_split минимальный выигрыш, описывающий разделение Полезное разделение деревьев управления
max_cat_group Найдите точки разделения на границах группы Когда количество категорий велико, их легко переобучить при поиске точек сегментации.

```python
# 设置几个参数
def lgb_cv(colsample_bytree, min_child_samples,reg_alpha,reg_lambda,min_split_gain,subsample_freq,

    num_leaves, subsample, max_depth,learning_rate,min_child_weight):
    model = lgb.LGBMClassifier(boosting_type='gbdt',objective='binary',
           colsample_bytree=float(colsample_bytree), learning_rate=float(learning_rate),
           min_child_samples=int(min_child_samples), min_child_weight=float(min_child_weight), 
           min_split_gain = float(min_split_gain),subsample_freq = int(subsample_freq),
           n_estimators=8000, n_jobs=-1, num_leaves=int(num_leaves),
           random_state=None, reg_alpha=float(reg_alpha), reg_lambda=float(reg_lambda),max_depth=int(max_depth),
           subsample=float(subsample))
    cv_score = cross_val_score(model, x, y, scoring="f1", cv=5).mean()
    return cv_score
# 使用贝叶斯优化
lgb_bo = BayesianOptimization(
        lgb_cv,
     {
            'learning_rate': (0.008, 0.01),
            'max_depth': (3, 10),
            'num_leaves': (31, 127),
            'min_split_gain':(0.0,0.4),
            'min_child_weight':(0.001,0.002),
            'min_child_samples':(18,22),
            'subsample':(0.6,1.0),
            'subsample_freq':(3,5),
            'colsample_bytree':(0.6,1.0),
            'reg_alpha':(0,0.5),
            'reg_lambda':(0,0.5)
        },



    )
lgb_bo.maximize(n_iter=1000)
lgb_bo.max
# 将优化好的参数带入进行使用



Визуализируйте процесс принятия решений

from IPython.display import Image

model = lgb.LGBMClassifier(num_leaves=64,
                           max_depth=10,
                           learning_rate=0.01,
                           boosting_type = 'goss',
                           n_estimators=10000,
                           subsample=0.8,
                           feature_fraction=0.8,
                           reg_alpha=0.5,
                           
                           reg_lambda=0.5,
                           random_state=seed,
                           metric="f1")
X_train = df_train[feature_names]
Y_train = df_train[ycol]
lgb_model = model.fit(X_train,
                          Y_train,
                          eval_names=['valid'],
                          eval_set=[(X_val, Y_val)],
                          verbose=10,
                          eval_metric='auc',
                          early_stopping_rounds=50)
import matplotlib.pyplot as plt
fig2 = plt.figure(figsize=(200, 200))
ax = fig2.subplots()
lgb.plot_tree(lgb_model, tree_index=1, ax=ax)
plt.show()


image.png

ROC-кривая

image.png

Распределение важности каждого атрибута

Путем проверки важности каждого атрибута в процессе принятия решений ранжируются лучшие поля.Дальнейшая разработка функций

image.png

слияние моделей

Простым взвешенным способом результаты трех моделей объединяются, чтобы избежать проблемы переобучения модели.

w_lgb = 0.333
w_xgb = 0.333
w_cbt = 0.333

oof['prob'] = oof['prob_lgb'] ** w_lgb * oof['prob_xgb'] ** w_xgb * oof['prob_cbt'] ** w_cbt
auc = roc_auc_score(oof['y1_is_purchase'], oof['prob'])

sub['label'] = sub['prob_lgb'] ** w_lgb * sub['prob_xgb'] ** w_xgb * sub['prob_cbt'] ** w_cbt
sub.head()

Просмотр объединенного результата

image.png

Суммировать

Поскольку это был первый раз, когда я официально участвовал в соревновании, я чувствовал себя немного ошеломленным перед данными 60 Вт. Постепенно просматривайте то, чему учили в классе, просматривая материалы. От проектирования функций до выбора модели и оптимизации параметров. В начале я построил свою собственную базовую линию с деревом решений. Затем перейдите к дереву повышения градиента и, наконец, объедините три модели XGBoost, LightGBM и CatBoost. Также научился использовать байесовскую оптимизацию параметров во время этого эксперимента. Дизайн этого эксперимента может быть не идеальным, и действительно есть много проблем, которые мы исправим и улучшим в будущем. Хотя эксперимент подошел к концу, мне еще предстоит пройти долгий путь, и я буду продолжать совершенствоваться шаг за шагом.

использованная литература

LightGBM: A Highly Efficient Gradient Boosting Decision Tree

Biau G, Devroye L, Lugosi G. Consistency of Random Forests and Other Averaging Classifiers.[J]. Journal of Machine Learning Research, 2008, 9(1):2015-2033.

Модель дерева решений, визуализация моделей XGBoost, LightGBM и CatBoost Краткое изложение алгоритма LightGBM

Примечания по настройке LightGBM

scikit-learn

Frazier P I. A tutorial on Bayesian optimization[J]. arXiv preprint arXiv:1807.02811, 2018.