В First Day Out Ти Гризли поет: Не позволяй им взять над тобой верх, просто получай доход. Это послужило вдохновением для написания этого поста, только с небольшой поправкой: Не позволяйте ML взять над вами верх, просто оптимизируйте доход. В этом посте будет описан процесс построения дерева решений на Python для оптимизации набора данных кредитных заявок для трех разных кредиторов. Наш набор данных по заявкам на получение кредита состоит из 100 тысяч строк заявок на получение кредита для кредиторов A, B и C. Некоторые заявки одобряются и приносят вознаграждение, которое кредитор платит нам за услугу предоставления одобренной заявки на получение кредита, а некоторые заявки отклоняются и не дают никаких результатов. награда. Наша цель — использовать дерево решений, чтобы узнать о процессе принятия решений каждым кредитором, а затем оптимизировать способ сопоставления заявителей с кредиторами, чтобы максимизировать общую сумму вознаграждения. Хотя в этом посте будет рассмотрено, как решить этот конкретный случай подачи заявки на кредит, алгоритм классификации и регрессионного теста (CART), подобный этому, может быть применен к любому варианту использования, где мы хотим улучшить решения по сопоставлению входных данных с потенциальными выходными данными для оптимизации для определенный результат.

Код и набор данных:

Код и набор данных, лежащие в основе этого поста, можно найти на моем github. Вот ссылка на репозиторий: Дерево решений для оптимизации Loan Bounty.

Экономическое обоснование:

Службы, которые сопоставляют заявителей на получение кредита с кредиторами (такие как Bankrate, Lendstart или CreditKarma), получают вознаграждение, когда кредитор одобряет заявку, которую ему отправляет служба сопоставления. Разные кредиторы платят разные вознаграждения за одобренный кредит. Эти службы собирают базовую информацию о заявителях, такую ​​​​как кредитный рейтинг, статус занятости, доход и многое другое, чтобы помочь лучше подобрать заявителя подходящего кредитора. Служба хочет максимизировать вознаграждение, которое они получают, сопоставляя заявителей с кредиторами, которые платят самую высокую награду за одобрение. Однако более высокая награда кредитора обычно соответствует более избирательному кредитору, который с меньшей вероятностью одобрит кредит, а заявители, которым отказано в кредите с одной услугой, с меньшей вероятностью будут использовать эту услугу для подачи заявки на другой кредит. Каждая служба должна попытаться максимизировать получаемое вознаграждение, одновременно направляя заявителя к кредитору, который, скорее всего, одобрит его заявку. Поскольку эти сервисы содержат объемы обученных данных о заявках на получение кредита, показывающие результат каждой заявки для разных кредиторов, этот вариант использования созрел для оптимизации с помощью деревьев решений.

Цель:

Наша цель — максимизировать общую сумму вознаграждения, которую мы получаем за подачу заявок на получение кредита, одобренных тремя разными кредиторами, а также увеличить общий уровень одобрения. У нас есть уже существующий набор данных (loan_data.csv), содержащий заявки на получение кредита и их результаты для трех разных кредиторов. Набор данных состоит из 6 функциональных переменных (так называемых предикторов, которые наша модель дерева решений будет использовать для прогнозирования результата заявки): Loan_Amount, FICO_SCORE, Employment_Status, Monthly_Gross_Income, Monthly_Housing_Payment и Ever_Bankrupt_or_Foreclose. Набор данных также содержит столбец кредитора, указывающий, с каким кредитором был сопоставлен заявитель; столбец «Утверждено», который является нашей двоичной целевой переменной (то есть выходными данными, которые хочет предсказать наше дерево решений); и столбец вознаграждения, показывающий, сколько нам заплатил кредитор, если вообще заплатил, за подачу заявки на кредит.

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

Кредиторы выплачивают следующие вознаграждения:

Кредитор 0: 250 долларов США.

Кредитор 1: 350 долларов США.

Кредитор 2: 150 долларов США.

Учитывая эти вознаграждения, наша стратегия оптимизации направлена ​​на то, чтобы сначала максимизировать количество одобренных кредитов от Кредитора 1, затем от Кредитора 0, а затем от Кредитора 2.

Что такое дерево решений?

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

Предположим, вы управляете бейсбольной командой. При выборе состава вам следует учитывать несколько факторов, в том числе базовый процент (OBP) каждого игрока в этом сезоне, есть ли у них горячая или холодная полоса, как они бьют по питчеру соперника, на каких позициях они могут играть на поле. , хорошо ли они бьют против питчера-левши/правши и так далее.

Дерево решений похоже на блок-схему, которая помогает вам принимать решения о том, кого и куда бить. Вы начинаете со своего списка нападающих. Первый вопрос может звучать так: «Выше ли их ОБП 0,400 в последних 7 играх?» Если ответ «да», вы следуете по ветке направо, если нет, вы следуете по ветке налево. Каждая из этих ветвей затем приводит к новому вопросу, например: «Является ли их средний показатель по сравнению с тем типом питчера, с которым вы сталкиваетесь, выше 0,285 в этом сезоне?» и так далее, пока вы не примете решение о порядке, который максимизирует ваши шансы на выигрыш очков.

Мы будем обучать 3 дерева решений (по одному для каждого кредитора) и использовать их для прогнозирования одобрения кредитов.

Настройка кода:

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

Сначала мы импортируем pandas, DecisionTreeClassifier и train_test_split.

Pandas — это библиотека Python, предоставляющая структуры данных и инструменты анализа. Мы будем использовать его объект DataFrame, который представляет собой двумерную таблицу данных со строками и столбцами, аналогичную электронным таблицам и таблицам SQL.

DecisionTreeClassifier — это класс из sklearn.tree, который позволяет нам создавать дерево решений. Функция train_test_split происходит из модуля sklearn.model_selection, и мы будем использовать ее, чтобы разделить наш набор данных на обучающий набор для обучения нашей модели и тестовый набор для проверки нашей модели.

import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split

На шаге 1 мы читаем наш набор данных из файла csv с помощью pandas и сохраняем его в объекте фрейма данных с именем df. Мы также определяем словарь, состоящий из значений вознаграждений для каждого кредитора.

# Step 1 - Import the data and define key variables 
# for each lender 
# Load the data from a CSV
df = pd.read_csv('/Users/loughlinrodd/Desktop/ai-problems/loan-bounty-optimization/loan_data.csv')

# Define the bounty for each lender
bounties = {0: 250, 1: 350, 2: 150}

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

# Step 2: Calculate the baseline approval rates
baseline_approval_rates = df.groupby('lender')['Approved'].mean()
print("Baseline Approval Rates:")
print(baseline_approval_rates)

Затем мы печатаем средний уровень одобрения каждого кредитора. Это позволяет нам увидеть начальный уровень одобрения для каждого кредитора:

На шаге 3 мы рассчитываем общую сумму вознаграждения, сопоставляя значения кредитора в df с соответствующим значением вознаграждения кредитора в bounties и создавая новый ряд, который является продуктом столбца «Утверждено» и вознаграждения конкретного кредитора, и суммируем этот ряд. Мы сохраняем эту сумму в total_bounty_before, а затем печатаем сумму вознаграждения, которая составляет 2 641 500 долларов США. Это та ценность, которую мы хотим максимизировать с помощью нашего дерева решений. Далее мы определяем словарь, в котором будут храниться общие суммы вознаграждений для каждого кредитора до оптимизации (это необходимо для того, чтобы мы могли сравнивать результаты для каждого кредитора до и после запуска оптимизации). Затем мы заполняем значения для каждого кредитора в этом словаре, перебирая каждого кредитора, чтобы вычислить количество одобренных кредитов для кредитора и умножить эту сумму на сумму вознаграждения этого кредитора.

# Step 3: Calculate the total of the bounty column by summing the product of the bounty for each lender and each approval
total_bounty_before = (df['Approved'] * df['lender'].map(bounties)).sum()
print(f"Total Bounty Before Decision Tree: {total_bounty_before}")

# Define a dictionary to store the total bounty per lender
total_bounty_per_lender_before = {0: 0, 1: 0, 2: 0}
# Iterate through each lender and calculate the total bounty
for lender in bounties.keys():
    # Filter the dataframe df to include only the rows for the lender in question where the loan is approved and access the number of rows with .shape[0]
    approved_loans = df[(df['lender'] == lender) & (df['Approved'] == 1)].shape[0]
    # Multiply the approved_loans by the bounty for the lender in question 
    total_bounty_per_lender_before[lender] = approved_loans * bounties[lender]
# Print the total bounty per lender before optimization
for lender, bounty in total_bounty_per_lender_before.items():
    print(f"Total bounty for lender {lender}: {bounty}")
We then print the values for total bounty per lender:

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

Четвертый шаг в настройке кода — предварительная обработка данных. Предварительная обработка данных — важный шаг в процессе применения машинного обучения к набору данных. Он включает в себя подготовку и очистку данных, чтобы сделать их пригодными для процесса машинного обучения. К счастью, большая часть работы по предварительной обработке, которую мы могли бы рассмотреть, например, обработка пропущенных значений и кодирование категориальных переменных (то есть преобразование нечисловых переменных в числовые), не требуется с имеющимся у нас набором данных. Наш код просто определяет 6 столбцов переменных функций и извлекает их в фрейм данных X. Затем он извлекает целевую переменную Approved в свой собственный фрейм данных Y.

# Step 4: Preprocess data for a decision tree - define the feature columns
feature_cols = ['Loan_Amount', 'FICO_SCORE', 'Employment_Status', 'Monthly_Gross_Income', 'Monthly_Housing_Payment', 'Ever_Bankrupt_or_Foreclose']
# extract feature_cols into dataframe X
X = df[feature_cols]
# extract the 'Approved' column into dataframe Y
y = df['Approved']

Процесс оптимизации:

Обучение деревьев решений: мы используем алгоритм CART для обучения отдельных деревьев решений для каждого кредитора, используя существующие данные о кредите. Наш код сначала определяет пустой словарь lender_models для хранения модели каждого кредитора. Затем мы перебираем каждого кредитора, фильтруем фрейм данных для строк, которые имеют для них отношение, и извлекаем переменные функции и цели в X_lender и y_lender соответственно. Затем мы используем функцию test_train_split, чтобы разделить данные на обучающий и тестовый наборы. Затем мы инициализируем clf DecisionTreeClassifier со значением random_state = 42. Это гарантирует, что мы получаем единообразные результаты каждый раз, когда запускаем код, что полезно для тестирования различных настроек алгоритма, чтобы увидеть влияние на производительность. Без этого наше дерево решений будет каждый раз использовать разные наборы тестирования и обучения, что приведет к несколько разным результатам. Затем мы вызываем clf.fit(X_train, y_train) — это дает алгоритму указание обучаться на наборах данных тестирования и обучения. Затем мы печатаем глубину каждого дерева и количество листьев — глубина может помочь нам показать, когда наше дерево становится слишком глубоким, что может привести к переобучению (то есть дерево хорошо работает на обучающих данных, но не на новых данных) из-за шум или случайность в данных. Количество листьев говорит нам, сколько решений принимает наше дерево, а также может быть индикатором переобучения. Наконец, мы сохраняем обученную модель обратно в словарь lender_models, который мы создали, с ключом кредитора, который мы только что обработали.

# Step 5: Create a decision tree for each lender and print details
lender_models = {}
# Iterate through each lender 
for lender in df['lender'].unique():
    # Filter the dataframe df so that only the rows for the rows for the current lender are held in lender_data
    lender_data = df[df['lender'] == lender]
    # Extract the feature_cols from lender_data into X_lender
    X_lender = lender_data[feature_cols]
    # Extract the 'Approved' column from lender_data into y_lender
    y_lender = lender_data['Approved']
    # Split the data for the current lender into training and testing sets using 20% of the data for testing. 
    X_train, X_test, y_train, y_test = train_test_split(X_lender, y_lender, test_size=0.2, random_state=42)
    # Set clf to a decision tree classififer object with random_state=42 to set a seed for the random number generator used by the decision tree model. 
    # This will make our results deterministic and uniform each time we run the code
    clf = DecisionTreeClassifier(random_state=42)
    # Fit clf to the training data
    clf.fit(X_train, y_train)
    # Print lender, depth of tree and number of leaves in tree
    print(f"Lender {lender}:")
    print(f"  Depth: {clf.tree_.max_depth}")
    print(f"  Number of leaves: {clf.tree_.n_leaves}")
    # Store the trained decision tree classifier in a dictionary with a key corresponding to that lender
    lender_models[lender] = clf

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

# Step 6: Optimize how applications are associated with lenders such that the total bounty is maximized
# Define variable for total bounty after optimizing applications
total_bounty_after = 0
# Define dictionary for number of approved loans for each lender after optimization
approved_loans_after = {0: 0, 1: 0, 2: 0}
# Define dictionary for total bounty coming from each lender
total_bounty_per_lender_after = {0: 0, 1: 0, 2: 0}

# Iterate through each row of the dataframe df
for index, row in df.iterrows():
    # Loop through the lenders in order of their bounty, descending
    for lender in [1, 0, 2]: 
        # Get the trained decision tree model for the current lender 
        model = lender_models[lender]
        # Use the model to predict the approval status of the loan in the current row of data by extracting the feature columns and feeding them to the model
        prediction = model.predict([row[feature_cols]])
        # Check if the loan is approved or not
        if prediction[0] == 1: # Loan approved
            # Increment total_bounty_after by the bounty of the approving lender
            total_bounty_after += bounties[lender]
            # Increment approved_loans_after for the approving lender
            approved_loans_after[lender] += 1
            # Incremenent the total_bounty_per_lender_after by the bounty amount for that lender
            total_bounty_per_lender_after[lender] += bounties[lender]
            break # Exit the loop if loan approved. Since loans are evaluated by highest bounty lender first, once we get an approval we can exit the loop.

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

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

Результаты:

Ключевым результатом этой оптимизации является существенное увеличение общей суммы вознаграждения до 7 297 250 долларов США. Адаптировав процесс подачи заявок к предпочтениям различных кредиторов, мы добились значительного увеличения общей суммы вознаграждения примерно в 2,76 раза после оптимизации.

Давайте посмотрим на вознаграждение от каждого кредитора до и после оптимизации:

И количество одобрений по каждому кредитору до и после оптимизации:

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

Подведение итогов:

Оптимизация вознаграждения по кредитам – это не просто теоретическое упражнение; у него есть реальные применения, которые могут привести к существенной финансовой выгоде. Используя Python и алгоритм CART от sklearn.tree, мы смогли создать интеллектуальную систему, которая направляет подачу заявок на кредит, что приводит к максимизации прибыли.

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

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