Пример с использованием данных о кредитоспособности Германии, моделей классификации и приложения Gradio.

Оглавление:

· 1. Введение:
1.1 Описание категорийных данных:
1.2 Импорт данных и библиотек
· 2. Исследовательский анализ данных (EDA) и подготовка данных
3. Применение и оценка моделей
3.1 Выбор функций
3.2 Применение модели с персонализированным конвейером
3.3 Оптимизация модели с помощью GridSearchCV и окончательные результаты
3.4 Сохранение модели
· 4. Разработка приложения Gradio и его развертывание на Hugging Face
4.1 Hugging Face

1. Введение:

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

Чтобы решить, какую модель машинного обучения использовать, мы протестируем 3 модели классификации: логистическую регрессию, классификатор дерева решений и классификатор случайного леса, которые доступны в библиотеке Python sklearn. Чтобы оптимизировать рабочий процесс и повысить производительность модели, мы создадим настраиваемый конвейер для приложения модели и будем использовать GridSearchCV для оптимизации параметров. По крайней мере, мы создадим наше приложение, используя библиотеку приложений Gradio, доступную на python. Конечным результатом станет приложение, которое прогнозирует кредитоспособность клиентов для банковских менеджеров и кредитных аналитиков.

Поскольку я бразилец, стоит отметить, что процесс выбора функций будет осуществляться в соответствии с правилами Центрального банка Бразилии.

Кроме того, мы будем использовать эту предыдущую работу Пенсильванского государственного университета в качестве ссылки в некоторых частях кода. Также ко всему проекту можно получить доступ на hugging и github.

1.1 Описание категорийных данных:

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

Creditability:
- 1: credit-worthy
- 0: not credit-worthy

Account Balance:
- 1: no running account
- 2: no balance or debit
- 3: 0 <= … < 200 DM
- 4: … >= 200 DM or checking account for at least 1 year

Payment Status of Previous Credit:
- 0: hesitant payment of previous credits
- 1: problematic running account / there are further credits running but at other banks
- 2: no previous credits / paid back all previous credits
- 3: no problems with current credits at this bank
- 4: paid back previous credits at this bank

Purpose:
- 0: other
- 1: new car
- 2: used car
- 3: items of furniture
- 4: radio / television
- 5: household appliances
- 6: repair
- 8: vacation
- 9: retraining
- 10: business

Value Savings/Stocks:
- 1: not available / no savings
- 2: < 100,- DM
- 3: 100,- <= … < 500,- DM
- 4: 500,- <= … < 1000,- DM
- 5: >= 1000,- DM

Length of current employment:
- 1: unemployed
- 2: <= 1 year
- 3: 1 <= … < 4 years
- 4: 4 <= … < 7 years
- 5: >= 7 years

Instalment per cent:
- 1: >= 35
- 2: 25 <= … < 35
- 3: 20 <= … < 25
- 4: < 20

Sex & Marital Status:
- 1: male: divorced / living apart
- 2: male: single
- 3: male: married / widowed
- 4: female

Guarantors:
- 1: none
- 2: Co-Applicant
- 3: Guarantor

Duration in Current address:
- 1: < 1 year
- 2: 1 <= … < 4 years
- 3: 4 <= … < 7 years
- 4: >= 7 years

Most valuable available asset:
- 1: not available / no assets
- 2: Car / Other
- 3: Savings contract with a building society / Life insurance
- 4: Ownership of house or land

Concurrent Credits:
- 1: at other banks
- 2: at department store or mail order house
- 3: no further running credits

Type of apartment:
- 1: free apartment
- 2: rented flat
- 3: owner-occupied flat

No of Credits at this Bank:
- 1: one
- 2: two or three
- 3: four or five
- 4: six or more

Occupation:
- 1: unemployed / unskilled with no permanent
- 2: unskilled with permanent residence
- 3: skilled worker / skilled employee / minor civil servant
- 4: executive / self-employed / higher civil servant

No of dependents:
- 1: 3 and more
- 2: 0 to 2

Telephone:
- 1: no
- 2: yes

Foreign Worker:
- 1: no
- 2: yes

1.2 Импорт данных и библиотек

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

import pandas as pd
import numpy as np
import plotly.express as px


from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.feature_selection import RFECV
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.metrics import f1_score, precision_score, recall_score, roc_curve, auc, confusion_matrix

import matplotlib.pyplot as plt
import seaborn as sns

import pickle
import gradio as gr

df = pd.read_csv('https://raw.githubusercontent.com/marcilioduarte/portfolio/main/completed/german_credit_risk_app/data/raw/german_credit.csv')

Примечание. Если вы только начинаете работать с Python и у вас не установлены эти библиотеки, вам необходимо сначала установить их. Погуглите (или в chatgpt) что-нибудь вроде «как установить pip install lib XXX в python с помощью windows/linux/mac», и все будет в порядке.

2. Разведочный анализ данных (EDA) и подготовка данных

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

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

И поскольку мы упомянули о подготовке данных, давайте объясним и это.

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

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

Таким образом, EDA и подготовка данных играют решающую роль в обеспечении успеха проектов машинного обучения. Однако важно отметить, что независимо от того, как вы это делаете, процесс EDA и манипулирования данными (обычно) является наиболее трудоемким.

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

df

df.info(null_counts=True)

for i in df.columns:
    print(f'\n{i}:\n')
    print(df[i].value_counts(normalize=True, ascending=True))
    print('\n-------------------\n')

df.hist(figsize=(20,15))

Теперь давайте сделаем нашу первую и простую манипуляцию.

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

Хотя данные относятся к немецкому кредиту, мое моделирование относится к бразильским банкам. Поэтому столбцы «Пол и семейное положение» и «Иностранный работник» я опускаю.

Я также уберу столбец «Телефон», потому что он не имеет отношения к нашему анализу.

df.drop(columns=['Sex & Marital Status', 'Foreign Worker', 'Telephone'], inplace=True)

Итак, у нас есть набор данных с 1000 строк и 18 столбцов без нулевых значений. Кроме того, у нас есть несколько столбцов, которые действительно являются числовыми (например, возраст), но большинство функций представляют собой категориальные тексты, закодированные в числовых символах.

Кроме того, как заметили преподаватели ПГУ, большинство этих категориальных столбцов не имеют хорошего распределения значений внутри своих категорий. Мы можем подтвердить это, посмотрев на результаты нашего шага «value_counts» и нашего шага гистограмм. Обычно эта проблема называется передискретизацией или недостаточной выборкой, поэтому мы должны исправить это перед построением нашей модели, и мы можем сделать это, сгруппировав несколько категорий, которые представляют небольшую часть наблюдений столбца. Они сделали это для 9 столбцов.

По их словам:

«В зависимости от пропорций ячеек, указанных в приведенной выше односторонней таблице, две или более ячеек объединяются для нескольких категориальных предикторов. Ниже мы представляем окончательную классификацию предикторов, которые потенциально могут иметь какое-либо влияние на Кредитоспособность
- Баланс на счете: Нет счета (1), Нет (Нет баланса) (2), Некоторый баланс ( 3)
- Статус платежа: Некоторые проблемы (1), Оплачен (2), Нет проблем (в этом банке) (3)
- Сбережения/стоимость запасов: нет, менее 100 немецких марок, [100, 1000] немецких марок, более 1000 немецких марок
- стаж работы: менее 1 года (включая безработных), [1, 4] , [4, 7), Старше 7
- Пол/Семейное положение: Мужчина разведен/холост, Мужчина женат/вдовец, Женщина
- Нет кредитов в этом банке: 1, более 1
- Поручитель: нет, да
- одновременные кредиты: другие банки или универмаги, нет
- Цель кредита: новый автомобиль, подержанный автомобиль, дом, другое

Итак, давайте сделаем то же самое здесь.

columns = [
 'Account Balance',
 'Payment Status of Previous Credit',
 'Value Savings/Stocks',
 'Length of current employment',
 'No of Credits at this Bank',
 'Guarantors',
 'Concurrent Credits',
 'Purpose']

account_balance_map = {4: 3} ## Some balance (3)
payment_status_map = {4: 3, 0: 1} ## No problems at this bank (3), ## Some problems (1)
value_savings_map = {4: 3} ## DM betwenn [100, 1000] (3)
employment_length_map = {2: 1} ## Below 1 year (or unemployed) (1)
no_credits_map = {3: 2, 4: 2} ## More than 1 (2)
guarantors_map = {3: 2} ## Guarantor (yes) (2)
concurrent_credits_map = {2: 1} ## Other Banks or Dept Stores (1)
purpose_map = {5: 3, 6: 3, 0: 4, 8: 4, 10: 4, 9: 4}  ## Home related (3),  ## Other (4)

mapping_dict = {
    'Account Balance': account_balance_map,
    'Payment Status of Previous Credit': payment_status_map,
    'Value Savings/Stocks': value_savings_map,
    'Length of current employment': employment_length_map,
    'No of Credits at this Bank': no_credits_map,
    'Guarantors': guarantors_map,
    'Concurrent Credits': concurrent_credits_map,
    'Purpose': purpose_map
}

for col in columns:
    df[col] = df[col].replace(mapping_dict[col])
    print(f'\n{col}:\n')
    print(pd.crosstab(index=df[col], columns=df['Creditability'], normalize=True, margins=True, margins_name='Total'))
    print('\n-------------------\n')   

Проверьте, как мы исправили проблему, глядя на итоги наших дистрибутивов.

Как говорили профессора:

“*Внизу показано сопоставление 9 предикторов, как определено выше, с кредитоспособностью. Пропорции, показанные в ячейках, являются пропорциями столбца, как и предельные пропорции. Например, у 30% из 1000 заявителей нет учетной записи, еще у 30% нет остатка, а у 40% есть какой-то остаток на счету. Среди тех, у кого нет счета, 135 признаны кредитоспособными, а 139 - некредитоспособными. В группе без остатка на счете 40 % оказались зачисленными, тогда как в группе с некоторым остатком только 1 % оказались не зачисленными».*

Сделав это, давайте создадим манекены для всех категориальных столбцов.

df.columns

categ_columns = ['Account Balance',
       'Payment Status of Previous Credit', 'Purpose',
       'Value Savings/Stocks', 'Length of current employment',
       'Instalment per cent', 'Guarantors', 'Duration in Current address',
       'Most valuable available asset', 'Concurrent Credits',
       'Type of apartment', 'No of Credits at this Bank', 'Occupation',
       'No of dependents']
df = pd.get_dummies(df, columns=categ_columns)
df

df.columns

3. Применение и оценка моделей

3.1 Выбор функций

Перед применением нашей модели нам нужно выбрать наши функции (X). Есть много способов сделать это, например, анализ корреляции между функциями и прогнозируемой переменной (и внутри других функций) или использование регуляризации лассо и гребня для наказания переменных с меньшим влиянием на нашу переменную Y, среди прочего. Но так как в данном случае у нас много переменных, я применю функцию Recursive Feature Elimination With Cross Validation (RFECV), доступную в sklearn.

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

Проверим на практике.

y=df['Creditability']
x=df.drop(columns='Creditability')


model = LogisticRegression()
rfecv = RFECV(model, min_features_to_select=10, cv=10, scoring='f1')
rfecv.fit(x, y)

nft = rfecv.n_features_
selected_features = x.columns[rfecv.support_]
print('Number of selected features:\n', nft, '\nSelected features:\n',selected_features)

Функции, которые дают нам лучший результат f1 в модели LREG, — это 22 функции, указанные выше.

y = df['Creditability']
x = df[['Account Balance_1', 'Account Balance_2', 'Account Balance_3',
       'Payment Status of Previous Credit_1',
       'Payment Status of Previous Credit_3', 'Purpose_1', 'Purpose_4',
       'Value Savings/Stocks_1', 'Value Savings/Stocks_3',
       'Value Savings/Stocks_5', 'Length of current employment_1',
       'Length of current employment_4', 'Instalment per cent_4',
       'Guarantors_1', 'Duration in Current address_1',
       'Duration in Current address_2', 'Most valuable available asset_1',
       'Most valuable available asset_4', 'Concurrent Credits_3',
       'Type of apartment_1', 'No of Credits at this Bank_1', 'Occupation_1']]

3.2 Применение модели с персонализированным конвейером

Теперь мы собираемся создать набор функций, которые сэкономят нам время при тестировании различных моделей. В конце мы объединим все эти функции в одну, как в конвейере, а затем применим ее.

Более подробно, наш код реализует конвейер машинного обучения, который обучает и оценивает различные модели классификации (логистическая регрессия, классификатор дерева решений и классификатор случайного леса) в заданном наборе данных.

Функция get_train_test() разбивает данные на наборы для обучения и тестирования, а функция model_apply() применяет модель к данным обучения и прогнозирует результаты для данных тестирования.

Функция evaluate_model() вычисляет различные метрики оценки (оценка F1, точность, полнота и ROC AUC) и визуализирует результаты с использованием матрицы путаницы и кривой ROC. При работе с классификационными моделями эти оценки являются одними из рекомендуемых.

Функция validation_with_cv() выполняет перекрестную проверку для оценки производительности модели, а функция pipeline() связывает все эти шаги вместе.

И, наконец, цикл for перебирает модели, применяет конвейер к каждой модели и сохраняет результаты в фрейме данных pandas. Проверьте это ниже:

def get_train_test(x, y, ts=0.7, rs=42):
  x_train, x_test, y_train, y_test = train_test_split(x, y, train_size=ts, random_state=rs)
  return x_train, x_test, y_train, y_test

def model_apply(model, x_train, y_train, x_test):
  model.fit(x_train, y_train)
  yHat = model.predict(x_test)
  return yHat

def evaluate_model(y_test, yHat, model):
  print(f'Evaluating {model} model:')
  print('\n')

  f1 = f1_score(y_test,yHat)
  precision = precision_score(y_test,yHat)
  recall = recall_score(y_test,yHat)
  fpr, tpr, threshold = roc_curve(y_test, yHat)
  roc_auc = auc(fpr, tpr)
  cfm = confusion_matrix(y_test, yHat)
  print(f'F1 Score: {f1.round(2)};\nPrecision: {precision.round(2)};\nRecall: {recall.round(2)};\nROC AUC score: {roc_auc.round(2)}')
  print('\n-------------------------------\n')

  fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10,4))  
  ax1.plot(fpr, tpr, color='darkorange', lw=2, label='ROC Curve')
  ax1.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')
  ax1.set_xlabel('False Positive Rate')
  ax1.set_ylabel('True Positive Rate')
  ax1.set_title('ROC Curve')
  ax1.legend(loc="lower right")  
  sns.heatmap(cfm, annot=True, cmap='Blues', ax=ax2)
  ax2.set_xlabel('Predicted')
  ax2.set_ylabel('True')
  ax2.set_title('Confusion Matrix')  
  plt.show()

  return f1, precision, recall, roc_auc, cfm  

def validation_with_cv(model, x_train, y_train):
    print(f'{model} results with cross-validation:')
    print('\n')

    scoring_metrics = ['precision', 'recall', 'f1']
    cvscores = {}
    for metric in scoring_metrics:
        scores = cross_val_score(model, x_train, y_train, scoring=metric, cv=10)
        cvscores[f'{metric}_std'] = np.std(scores).round(2)
        cvscores[f'mean_{metric}'] = np.mean(scores).round(2)
    
    print('Cross-validation results:')
    for metric, score in cvscores.items():
        print(f'{metric}: {score}')
    print('------------------\n')  
  
    return cvscores

def pipeline(x, y, model):
    x_train, x_test, y_train, y_test = get_train_test(x, y)
    yHat = model_apply(model, x_train, y_train, x_test)
    f1, precision, recall, roc_auc, cfm = evaluate_model(y_test, yHat, model)
    cvscores = validation_with_cv(model, x_train, y_train)
    return f1, precision, recall, roc_auc, cfm, cvscores['f1_std'], cvscores['mean_f1'], cvscores['precision_std'], cvscores['mean_precision'], cvscores['recall_std'], cvscores['mean_recall']

lreg = LogisticRegression()
dtc = DecisionTreeClassifier()
rfc = RandomForestClassifier()

models = {'Logistic Regression': lreg, 
     'Decision Tree Classifier': dtc, 
     'Random Forest Classifier': rfc}

first_results = []
for model_name, model in models.items():
    f1, precision, recall, roc_auc, cfm, f1_std, mean_f1, precision_std, mean_precision, recall_std, mean_recall = pipeline(x, y, model=model)
    first_results.append({'Model': model_name,
                'CV Mean Precision': mean_precision.round(2),
                'CV Mean F1': mean_f1.round(2),
                'CV Mean Recall': mean_recall.round(2),          
                'Test Precision': precision.round(2),
                'Test F1': f1.round(2),
                'Test Recall': recall.round(2),
                })
print('------------------------\nTable of results:\n')
first_results = pd.DataFrame(first_results)
first_results
lreg = LogisticRegression()
dtc = DecisionTreeClassifier()
rfc = RandomForestClassifier()

models = {'Logistic Regression': lreg, 
     'Decision Tree Classifier': dtc, 
     'Random Forest Classifier': rfc}

first_results = []
for model_name, model in models.items():
    f1, precision, recall, roc_auc, cfm, f1_std, mean_f1, precision_std, mean_precision, recall_std, mean_recall = pipeline(x, y, model=model)
    first_results.append({'Model': model_name,
                'CV Mean Precision': mean_precision.round(2),
                'CV Mean F1': mean_f1.round(2),
                'CV Mean Recall': mean_recall.round(2),          
                'Test Precision': precision.round(2),
                'Test F1': f1.round(2),
                'Test Recall': recall.round(2),
                })
print('------------------------\nTable of results:\n')
first_results = pd.DataFrame(first_results)
first_results

Основываясь на быстром анализе нашей первой таблицы результатов, мы можем получить ценную информацию:

1. Модель Decision Tree Classifier (DTC) показала худшие результаты, независимо от того, рассматриваем ли мы модели с перекрестной проверкой или с тестовыми данными.

2. При сравнении моделей логистической регрессии (LR) и классификатора случайного леса (RFC) мы заметили, что логистическая регрессия превосходит RFC почти по всем проанализированным показателям, за исключением показателя CV Mean Precision.

3. Кроме того, чтобы улучшить наши показатели точности, мы должны попробовать некоторые стратегии для достижения лучших результатов. Точность — это метрика, которая измеряет долю истинно положительных результатов среди всех случаев, прогнозируемых как положительные. В нашей модели прогнозируемая переменная равна 1, когда клиент кредитоспособен, и 0, когда он не кредитоспособен. Следовательно, истинные срабатывания — это случаи, когда модель правильно идентифицирует кредитоспособного клиента, а ложные срабатывания — это случаи, когда модель неправильно идентифицирует некредитоспособного клиента как кредитоспособного.

Давайте попробуем добиться минимального показателя точности 0,8, что означает, что наша модель должна давать хорошие рекомендации в отношении кредитоспособности по крайней мере в 80% случаев и рекомендации о рисках в 20% случаев, когда модель ошибочно идентифицирует рискового клиента как нерискового. Для этого мы попытаемся оптимизировать два лучших параметра нашей модели с помощью GridSearchCV.

3.3 Оптимизация модели с помощью GridSearchCV и окончательные результаты

Теперь мы выполним настройку гиперпараметров для наших лучших моделей (LRC и RFC), используя метод GridSearchCV из библиотеки scikit-learn. Гиперпараметры — это настраиваемые параметры, которые не изучаются во время обучения модели, и их часто задают до начала обучения. Цель настройки гиперпараметров — найти оптимальную комбинацию гиперпараметров, обеспечивающую наилучшую производительность модели в нашем наборе данных.

Начиная с модели LRC, в нашем коде мы определим словарь гиперпараметров под названием «lg_params». Первым гиперпараметром будет «fit_intercept», который является логическим значением (true/false), определяющим, должна ли модель включать термин перехвата в уравнение логистической регрессии. Второй гиперпараметр, «штраф», определяет тип регуляризации, применяемой к модели. Есть четыре варианта: «l1», «l2», «elasticnet» и «Нет». «l1» и «l2» соответствуют регуляризации L1 и L2 соответственно, а «elasticnet» представляет собой комбинацию обоих. «Нет» означает, что регуляризация применяться не будет.

Примечание. Регуляризация — это метод, используемый для предотвращения переобучения путем добавления штрафного члена к функции потерь модели во время обучения. Функция потерь (или затрат) – это показатель того, насколько модель соответствует данным. Условие штрафа побуждает модель иметь меньшие веса или более простые коэффициенты, что помогает избежать подгонки шума в обучающих данных и вместо этого хорошо обобщать новые данные. Он обычно используется в логистической регрессии, но также используется в других типах моделей. Двумя наиболее популярными типами регуляризации являются регуляризация L1 (также известная как Lasso) и регуляризация L2 (также известная как Ridge). Регуляризация L1 добавляет штраф, пропорциональный абсолютному значению весов, а регуляризация L2 добавляет штраф, пропорциональный квадрату весов

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

Например, алгоритм «lbfgs» использует квазиньютоновский метод, который аппроксимирует матрицу Гессе функции стоимости и имеет хорошую производительность для наборов данных малого и среднего размера. Алгоритм «liblinear» использует метод координатного спуска, который эффективен для данных высокой размерности. Алгоритм «прогиба» использует метод стохастического градиентного спуска, который может обрабатывать большие наборы данных. Алгоритм «saga» является расширением «sag», который может справиться с штрафом за регуляризацию l1.

Четвертый гиперпараметр «max_iter» определяет максимальное количество итераций, которое алгоритм оптимизации должен выполнить перед остановкой. Пятый гиперпараметр, «random_state», определяет случайное начальное число, используемое для инициализации генератора случайных чисел.

Затем мы создадим экземпляр класса GridSearchCV, передав модель логистической регрессии («lreg»), словарь гиперпараметров («lg_params»), схему перекрестной проверки с 10 кратностями («cv=10») и оценочная метрика, подлежащая оптимизации («точность»). Наконец, мы подгоним объект GridSearchCV к обучающим данным (x_train, y_train) для поиска оптимальных гиперпараметров, которые максимизируют показатель точности.

x_train, x_test, y_train, y_test = get_train_test(x,y)
lg_params = {
    'fit_intercept': [True, False],
    'penalty': ['l1', 'l2', 'elasticnet', None],
    'solver': ['lbfgs', 'liblinear', 'newton-cg', 'newton-cholesky', 'sag', 'saga'],   
    'max_iter': [50, 100, 300],     
    'random_state': [None, 42]
            }

grid_lg = GridSearchCV(lreg, lg_params, cv=10, scoring='precision')

grid_lg.fit(x_train, y_train)

Теперь мы сделаем то же самое для моделей RFC, но в этом случае нам понадобится другой словарь гиперпараметров. Первый гиперпараметр, который мы будем использовать, «n_estimators», определяет количество деревьев решений, которые будут использоваться в случайном лесу. Мы будем тестировать значения 10, 50 и 100.

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

Второй гиперпараметр «max_depth» определяет максимальную глубину (в ветвях) каждого дерева решений в случайном лесу. Более высокая максимальная глубина может привести к переобучению, а более низкая максимальная глубина может привести к недостаточной подгонке. Мы будем тестировать следующие значения: None (неограниченная глубина), 5 и 10.

Третий гиперпараметр «min_samples_split» определяет минимальное количество выборок (наблюдений), необходимых для разделения внутреннего узла в каждом дереве решений. Более высокое значение может привести к недостаточной подгонке, а более низкое значение может привести к переобучению. Мы будем тестировать значения 2, 10 и 100.

Четвертый гиперпараметр, «random_state», определяет начальное число, используемое генератором случайных чисел. Это гарантирует, что модель случайного леса может быть воспроизведена точно так же. Мы будем тестировать следующие значения: None (случайное) и 42 (фиксированное начальное число).

Примечание. Число 42 обычно используется в качестве параметра случайного состояния в алгоритмах машинного обучения в знак уважения к книге Дугласа Адамса «Автостопом по Галактике». В книге число 42 известно как «Ответ на главный вопрос жизни, Вселенной и всего остального». Хотя использование числа 42 не дает никаких практических преимуществ для производительности алгоритма, оно стало традицией среди специалистов по обработке и анализу данных и специалистов по машинному обучению как забавная и занудная ссылка на книгу.

rf_params = {
    'n_estimators'     : [10, 50, 100],
    'max_depth'        : [None, 5, 10],
    'min_samples_split': [2, 10, 100],
    'random_state'     : [None, 42]
}
grid_rf = GridSearchCV(rfc, rf_params, cv=10, scoring='precision')
grid_rf.fit(x_train, y_train)

Теперь распечатаем результаты:

print('Logistic Regression Results:')
lg_best_param = grid_lg.best_params_
lg_best_score = grid_lg.best_score_
print(f'GridSearch best params: \n{lg_best_param}.\nGridSearch best precision score: {lg_best_score.round(2)}.')
print('\n--------------------------------\n')
print('Random Forest Classifier Results:')
rf_best_param = grid_rf.best_params_
rf_best_score = grid_rf.best_score_
print(f'GridSearch best params: \n{rf_best_param}.\nGridSearch best precision score: {rf_best_score.round(2)}.')

Наконец, давайте оценим оптимизированные модели и добавим окончательные результаты в фрейм данных.

lreg = LogisticRegression(fit_intercept=True, max_iter=50, penalty='l2', random_state=None, solver='lbfgs')
lrmodel = lreg.fit(x_train, y_train)
lr_final_yHat = lrmodel.predict(x_test)

lr_cvscores_final_precision = cross_val_score(lreg, x_train, y_train, scoring='precision', cv=10)
lr_cvscores_std_final_precision = np.std(lr_cvscores_final_precision).round(2)
lr_cvscores_mean_final_precision = np.mean(lr_cvscores_final_precision).round(2)

lr_cvscores_final_f1 = cross_val_score(lreg, x_train, y_train, scoring='f1', cv=10)
lr_cvscores_std_final_f1 = np.std(lr_cvscores_final_f1).round(2)
lr_cvscores_mean_final_f1 = np.mean(lr_cvscores_final_f1).round(2)

lr_cvscores_final_recall = cross_val_score(lreg, x_train, y_train, scoring='recall', cv=10)
lr_cvscores_std_final_recall = np.std(lr_cvscores_final_recall).round(2)
lr_cvscores_mean_final_recall = np.mean(lr_cvscores_final_recall).round(2)

lr_test_f1, lr_test_precision, lr_test_recall, lr_test_roc_auc, lr_test_cfm = evaluate_model(y_test=y_test, yHat=lr_final_yHat, model=lrmodel)

final_results = []
final_results.append({'Model': 'Optimized LR',
                   'CV Mean Precision'   : lr_cvscores_mean_final_precision,
                   'CV Mean F1'     : lr_cvscores_mean_final_f1,
                   'CV Mean Recall.': lr_cvscores_mean_final_recall,
                   'Test Precision' : lr_test_precision.round(2),
                   'Test F1' : lr_test_f1.round(2),
                   'Test Recall' : lr_test_recall.round(2)
                   })
rfc = RandomForestClassifier(max_depth=None, min_samples_split=10, n_estimators=10, random_state=42)
rfcmodel = rfc.fit(x_train, y_train)
rfc_final_yHat = rfcmodel.predict(x_test)

rfc_cvscores_final_precision = cross_val_score(rfc, x_train, y_train, scoring='precision', cv=10)
rfc_cvscores_std_final_precision = np.std(rfc_cvscores_final_precision).round(2)
rfc_cvscores_mean_final_precision = np.mean(rfc_cvscores_final_precision).round(2)

rfc_cvscores_final_f1 = cross_val_score(rfc, x_train, y_train, scoring='f1', cv=10)
rfc_cvscores_std_final_f1 = np.std(rfc_cvscores_final_f1).round(2)
rfc_cvscores_mean_final_f1 = np.mean(rfc_cvscores_final_f1).round(2)

rfc_cvscores_final_recall = cross_val_score(rfc, x_train, y_train, scoring='recall', cv=10)
rfc_cvscores_std_final_recall = np.std(rfc_cvscores_final_recall).round(2)
rfc_cvscores_mean_final_recall = np.mean(rfc_cvscores_final_recall).round(2)

rfc_test_f1, rfc_test_precision, rfc_test_recall, rfc_test_roc_auc, rfc_test_cfm = evaluate_model(y_test=y_test, yHat=rfc_final_yHat, model=rfcmodel)

final_results.append({'Model': 'Optimized RF',
                   'CV Mean Precision'   : rfc_cvscores_mean_final_precision,
                   'CV Mean F1'     : rfc_cvscores_mean_final_f1,
                   'CV Mean Recall.': rfc_cvscores_mean_final_recall,
                   'Test Precision' : rfc_test_precision.round(2),
                   'Test F1' : rfc_test_f1.round(2),
                   'Test Recall' : rfc_test_recall.round(2)
                   })
final_results = pd.DataFrame(final_results)
final_results

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

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

Хотя мы не смогли достичь желаемой средней точности 0,8 с перекрестной проверкой, модель по-прежнему выглядит очень хорошо. Итак, в итоге сохраним RFC-модель с оптимизированными параметрами с помощью pickle и приступим к разработке приложения.

3.4 Сохранение модели

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

path =  'german_credit_risk\model\model.pickle'
with open(path, 'wb' ) as file:
    pickle.dump(rfcmodel,file)

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

files = {'x_train':'x_train.parquet', 
         'x_test':'x_test.parquet', 
         'y_train':'y_train.parquet', 
         'y_test':'y_test.parquet',
         'rfc_final_yHat': 'yhat.parquet'}

clean_path = 'german_credit_risk\data\processed'

for key, value in files.items():
    if (key=='x_train') | (key =='x_test'):
        df = locals()[key]
        df.to_parquet(clean_path + '\\' + value)
    elif (key=='rfc_final_yHat'):
        df = pd.DataFrame(locals()[key])
        df.rename(columns={0:'yhat'}, inplace=True)
        df.to_parquet(clean_path + '\\' + value)
    else:
        df = pd.DataFrame(locals()[key])
        df.to_parquet(clean_path + '\\' + value)

Примечание. Файлы .parquet занимают меньше места, чем файлы .csv. Его рекомендуется использовать, особенно при работе с большими объемами данных.

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

4. Разработка приложения Gradio и его развертывание на Hugging Face

Наконец, давайте создадим наше приложение с интерфейсом Gradio. Причина, по которой мы будем использовать gradio вместо Streamlit, flask или dash, заключается в его простоте и доступности. Gradio — это удобная библиотека, которая позволяет нам быстро создавать и развертывать пользовательские веб-интерфейсы для моделей машинного обучения, не требуя большого опыта веб-разработки. С Gradio мы можем легко определить интерфейсы ввода и вывода для наших моделей, настроить пользовательский интерфейс с помощью различных виджетов и развернуть модель с помощью нескольких строк кода. Gradio также предоставляет облачную платформу для развертывания, которая позволяет нам делиться нашими моделями с другими, просто делясь URL-адресом. Это делает его идеальным выбором для тех, кто хочет быстро создавать свои модели машинного обучения и делиться ими с более широкой аудиторией.

Применять его на самом деле проще, чем кажется. Все, что нам нужно сделать, это создать функцию, которая будет использовать наш файл pickle для прогнозирования, а затем нам просто нужно настроить интерфейс нашего приложения, и для этого gradio предоставляет набор готовых компонентов, которые можно легко настроить, например ползунки, текстовые поля, флажки, уценки, выпадающие меню и загрузчики изображений. Пользователи также могут создавать собственные компоненты с помощью собственных HTML, CSS и JavaScript. Доступ к результирующему интерфейсу можно получить из веб-браузера и использовать как URL-адрес или встроить в веб-страницу. В целом компоненты Gradio предоставляют удобный способ создания интерактивных и удобных интерфейсов для моделей машинного обучения.

Хотя сообщество Gradio еще не очень велико из-за своего возраста, библиотека имеет очень хорошую документацию. Подробнее об этом можно узнать здесь.

Сейчас. Давайте сделаем это.

## Creating our function based on our 22 variables.

def predict_credit_worthiness(name, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22):
    path =  'german_credit_risk/model/model.pickle'
    greet = 'Hey, ' + name + '!' ## greeting the user.
    with open(path, 'rb') as file:
        model = pickle.load(file)
        inputs = {'Account Balance_1': int(x1),
                  'Account Balance_2': int(x2),
                  'Account Balance_3': int(x3),
                  'Payment Status of Previous Credit_1': int(x4),
                  'Payment Status of Previous Credit_3': int(x5),
                  'Purpose_1': int(x6),
                  'Purpose_4': int(x7),
                  'Value Savings/Stocks_1': int(x8),
                  'Value Savings/Stocks_3': int(x9),
                  'Value Savings/Stocks_5': int(x10),
                  'Length of current employment_1': int(x11),
                  'Length of current employment_4': int(x12),
                  'Instalment per cent_4': int(x13),
                  'Guarantors_1': int(x14),
                  'Duration in Current address_1': int(x15),
                  'Duration in Current address_2': int(x16),
                  'Most valuable available asset_1': int(x17),
                  'Most valuable available asset_4': int(x18),
                  'Concurrent Credits_3': int(x19),
                  'Type of apartment_1': int(x20),
                  'No of Credits at this Bank_1': int(x21),
                  'Occupation_1': int(x22)
                  }
        prediction = model.predict([list(inputs.values())])
        
    y_test = pd.read_parquet('german_credit_risk/data/processed/y_test.parquet')
    y_test = y_test.squeeze()

    yhat = pd.read_parquet('german_credit_risk/data/processed/yhat.parquet')
    yhat = yhat.squeeze()
    
    precision = precision_score(y_test, yhat).round(2)
    recall = recall_score(y_test, yhat).round(2)
    f1 = f1_score(y_test, yhat).round(2)

    features_names =  ['No account', 'No balance', 'Some balance', 'No credit problems', 
                       'Some credit problems', 'New car', 'Other purpose', 'No savings', 
                       'DM betwenn [100, 1000]', 'DM >= 1000', 'Employment: <1 year (or unemployed)', 'Employment: 4<x<7 years', 
                       'Installment smaller than 20%', 'No guarantors', 'Less than a year in same address', '1<x<4 years in address', 
                       'Not available / no assets', 'Ownership of house or land',  'No further running credits', 'Free ap', 
                       'One credit at thins bank','Unemployed or unskilled']
    importance = model.feature_importances_
    data = pd.DataFrame()
    data['Feature Importance'] = importance
    data['Feature'] = features_names
    p = px.bar(data, y='Feature Importance', x='Feature', width=1200, height=500)
    
    cfm = confusion_matrix(y_test, yhat)
    cfm_plot = px.imshow(cfm,
                x=['Predicted 0', 'Predicted 1'],
                y=['Actual 0', 'Actual 1'],
                color_continuous_scale='Blues',
                labels=dict(x="Predicted", y="Actual", color="Count"),
                text_auto=True)    
    
    if prediction == 1:
        return (greet + ' According to our model, your client is eligible for the loan.', 
                'Precision: '+ str(precision), 
                'Recall: '+ str(recall), 
                'F1 Score: '+ str(f1), 
                p, 
                cfm_plot)
    else:
        return (greet + ' Unfortunately, according to our model, your client is not eligible for the loan for now :(.', 
                'Precision: '+ str(precision), 
                'Recall: '+ str(recall), 
                'F1 Score: '+ str(f1), 
                p, 
                cfm_plot)
## creating the interface

with gr.Blocks() as demo:
    gr.Markdown('# Credit Worthiness Prediction')
    gr.Markdown("""
                To predict our clients' creditworthiness, please use this application as follows:
                
                1. Enter your name and navigate through the client's information tabs. Select the boxes that best match your client's characteristics. Leave blank if none apply.

                2. Once completed, click 'Predict' to determine if the client is creditworthy.
                """)
    with gr.Accordion('Name'):
        name = gr.Textbox(lines=1, label='Your name')
    with gr.Accordion("Enter your client's information"):
        with gr.Tab('Account Balance'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x1 = gr.Checkbox(1, label='No account')
            x2 = gr.Checkbox(0, label='No balance')
            x3 = gr.Checkbox(0, label='Some balance')
        with gr.Tab('Payment status of previous credit'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x4 = gr.Checkbox(1, label='Some problems')
            x5 = gr.Checkbox(0, label='No problems in this bank')
        with gr.Tab('Purpose'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x6 = gr.Checkbox(1, label='New car')
            x7 = gr.Checkbox(0, label='Other')
        with gr.Tab('Value savings/stocks'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x8 = gr.Checkbox(1, label='No savings')
            x9 = gr.Checkbox(0, label='DM betwenn [100, 1000]')
            x10 = gr.Checkbox(0, label='DM >= 1000')
        with gr.Tab('Length of current employment'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x11 = gr.Checkbox(1, label='Below 1 year (or unemployed)')
            x12 = gr.Checkbox(0, label='Between 4 and 7 years')
        with gr.Tab('Instalment per cent'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x13 = gr.Checkbox(0, label='Smaller than 20%')
        with gr.Tab('Guarantors'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x14 = gr.Checkbox(0, label='No guarantors')
        with gr.Tab('Duration in current address'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x15 = gr.Checkbox(1, label='Less than a year')
            x16 = gr.Checkbox(0, label='Between 1 and 4 years')
        with gr.Tab('Most valuable available asset'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x17 = gr.Checkbox(1, label='Not available / no assets')
            x18 = gr.Checkbox(0, label='Ownership of house or land')
        with gr.Tab('Concurrent credits'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x19 = gr.Checkbox(0, label='No further running credits')
        with gr.Tab('Type of apartment'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x20 = gr.Checkbox(0, label='Free apartment')
        with gr.Tab('Number of credits at this Bank'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x21 = gr.Checkbox(0, label='One credit')        
        with gr.Tab('Occupation'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x22 = gr.Checkbox(0, label='Unemployed or unskilled with no permanent') 
    predict_button = gr.Button('Predict')
    prediction_output = gr.Label(num_top_classes=2)
    with gr.Accordion('Metrics and plots'):
        with gr.Tab('Metrics'):
            with gr.Row():
                precision_output = gr.Label()
            with gr.Row():
                recall_output = gr.Label()
            with gr.Row():
                f1_output = gr.Label()
        with gr.Tab('Feature Importances'):
            fimp_output = gr.Plot()
        with gr.Tab('Confusion Matrix'):
            cfm_output = gr.Plot()
    predict_button.click(fn=predict_credit_worthiness,
                         inputs=[name, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22],
                         outputs=[prediction_output,precision_output, recall_output, f1_output, fimp_output, cfm_output])
    gr.Markdown('''
                Want to work in a project together or have interest in my services? Reach me:
                [Linkedin](https://www.linkedin.com/in/marcilioduarte98/)
                [Github](https://github.com/marcilioduarte)
                @marcilioduarte | Economics and Data Science
                ''')
demo.launch()

Примечание: вы можете использовать demo.launch(share=True), если хотите опубликовать свое приложение бесплатно в течение 72 часов с общедоступным URL-адресом, предоставленным gradio.

Вывод должен быть таким:

4.1 Обнимание лица

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

Примечание. Чтобы создать приложение, ваше приложение должно называться app.py, и вы должны создать файл "requirements.txt" с библиотеками, которые потребуются вашему файлу app.py.

Вот шаг за шагом с обниманием лица:

1. Создайте файл app.py на основе вашей модели и функции приложения следующим образом:

## IMPORTING LIBS

import pandas as pd
import plotly.express as px

from sklearn.metrics import f1_score, precision_score, recall_score, confusion_matrix

import pickle
import gradio as gr

## CREATING FUNCTION

def predict_credit_worthiness(name, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22):
    path =  'german_credit_risk/model/model.pickle'
    greet = 'Hey, ' + name + '!'
    with open(path, 'rb') as file:
        model = pickle.load(file)
        inputs = {'Account Balance_1': int(x1),
                  'Account Balance_2': int(x2),
                  'Account Balance_3': int(x3),
                  'Payment Status of Previous Credit_1': int(x4),
                  'Payment Status of Previous Credit_3': int(x5),
                  'Purpose_1': int(x6),
                  'Purpose_4': int(x7),
                  'Value Savings/Stocks_1': int(x8),
                  'Value Savings/Stocks_3': int(x9),
                  'Value Savings/Stocks_5': int(x10),
                  'Length of current employment_1': int(x11),
                  'Length of current employment_4': int(x12),
                  'Instalment per cent_4': int(x13),
                  'Guarantors_1': int(x14),
                  'Duration in Current address_1': int(x15),
                  'Duration in Current address_2': int(x16),
                  'Most valuable available asset_1': int(x17),
                  'Most valuable available asset_4': int(x18),
                  'Concurrent Credits_3': int(x19),
                  'Type of apartment_1': int(x20),
                  'No of Credits at this Bank_1': int(x21),
                  'Occupation_1': int(x22)
                  }
        prediction = model.predict([list(inputs.values())])
        
    y_test = pd.read_parquet('german_credit_risk/data/processed/y_test.parquet')
    y_test = y_test.squeeze()

    yhat = pd.read_parquet('german_credit_risk/data/processed/yhat.parquet')
    yhat = yhat.squeeze()
    
    precision = precision_score(y_test, yhat).round(2)
    recall = recall_score(y_test, yhat).round(2)
    f1 = f1_score(y_test, yhat).round(2)

    features_names =  ['No account', 'No balance', 'Some balance', 'No credit problems', 
                       'Some credit problems', 'New car', 'Other purpose', 'No savings', 
                       'DM betwenn [100, 1000]', 'DM >= 1000', 'Employment: <1 year (or unemployed)', 'Employment: 4<x<7 years', 
                       'Installment smaller than 20%', 'No guarantors', 'Less than a year in same address', '1<x<4 years in address', 
                       'Not available / no assets', 'Ownership of house or land',  'No further running credits', 'Free ap', 
                       'One credit at thins bank','Unemployed or unskilled']
    importance = model.feature_importances_
    data = pd.DataFrame()
    data['Feature Importance'] = importance
    data['Feature'] = features_names
    p = px.bar(data, y='Feature Importance', x='Feature', width=1200, height=500)
    
    cfm = confusion_matrix(y_test, yhat)
    cfm_plot = px.imshow(cfm,
                x=['Predicted 0', 'Predicted 1'],
                y=['Actual 0', 'Actual 1'],
                color_continuous_scale='Blues',
                labels=dict(x="Predicted", y="Actual", color="Count"),
                text_auto=True)    
    
    if prediction == 1:
        return (greet + ' According to our model, your client is eligible for the loan.', 
                'Precision: '+ str(precision), 
                'Recall: '+ str(recall), 
                'F1 Score: '+ str(f1), 
                p, 
                cfm_plot)
    else:
        return (greet + ' Unfortunately, according to our model, your client is not eligible for the loan for now :(.', 
                'Precision: '+ str(precision), 
                'Recall: '+ str(recall), 
                'F1 Score: '+ str(f1), 
                p, 
                cfm_plot)
    
## creating the interface

with gr.Blocks() as demo:
    gr.Markdown('# Credit Worthiness Prediction')
    gr.Markdown("""
                To predict our clients' creditworthiness, please use this application as follows:
                
                1. Enter your name and navigate through the client's information tabs. Select the boxes that best match your client's characteristics. Leave blank if none apply.

                2. Once completed, click 'Predict' to determine if the client is creditworthy.
                """)
    with gr.Accordion('Name'):
        name = gr.Textbox(lines=1, label='Your name')
    with gr.Accordion("Enter your client's information"):
        with gr.Tab('Account Balance'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x1 = gr.Checkbox(1, label='No account')
            x2 = gr.Checkbox(0, label='No balance')
            x3 = gr.Checkbox(0, label='Some balance')
        with gr.Tab('Payment status of previous credit'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x4 = gr.Checkbox(1, label='Some problems')
            x5 = gr.Checkbox(0, label='No problems in this bank')
        with gr.Tab('Purpose'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x6 = gr.Checkbox(1, label='New car')
            x7 = gr.Checkbox(0, label='Other')
        with gr.Tab('Value savings/stocks'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x8 = gr.Checkbox(1, label='No savings')
            x9 = gr.Checkbox(0, label='DM betwenn [100, 1000]')
            x10 = gr.Checkbox(0, label='DM >= 1000')
        with gr.Tab('Length of current employment'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x11 = gr.Checkbox(1, label='Below 1 year (or unemployed)')
            x12 = gr.Checkbox(0, label='Between 4 and 7 years')
        with gr.Tab('Instalment per cent'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x13 = gr.Checkbox(0, label='Smaller than 20%')
        with gr.Tab('Guarantors'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x14 = gr.Checkbox(0, label='No guarantors')
        with gr.Tab('Duration in current address'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x15 = gr.Checkbox(1, label='Less than a year')
            x16 = gr.Checkbox(0, label='Between 1 and 4 years')
        with gr.Tab('Most valuable available asset'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x17 = gr.Checkbox(1, label='Not available / no assets')
            x18 = gr.Checkbox(0, label='Ownership of house or land')
        with gr.Tab('Concurrent credits'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x19 = gr.Checkbox(0, label='No further running credits')
        with gr.Tab('Type of apartment'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x20 = gr.Checkbox(0, label='Free apartment')
        with gr.Tab('Number of credits at this Bank'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x21 = gr.Checkbox(0, label='One credit')        
        with gr.Tab('Occupation'):
            gr.Markdown('Select only one option. Leave all boxes blank if none of the options fits the client.')
            x22 = gr.Checkbox(0, label='Unemployed or unskilled with no permanent') 
    predict_button = gr.Button('Predict')
    prediction_output = gr.Label(num_top_classes=2)
    with gr.Accordion('Metrics and plots'):
        with gr.Tab('Metrics'):
            with gr.Row():
                precision_output = gr.Label()
            with gr.Row():
                recall_output = gr.Label()
            with gr.Row():
                f1_output = gr.Label()
        with gr.Tab('Feature Importances'):
            fimp_output = gr.Plot()
        with gr.Tab('Confusion Matrix'):
            cfm_output = gr.Plot()
    predict_button.click(fn=predict_credit_worthiness,
                         inputs=[name, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22],
                         outputs=[prediction_output,precision_output, recall_output, f1_output, fimp_output, cfm_output])
    gr.Markdown('''
                Want to work in a project together or have interest in my services? Reach me:
                [Linkedin](https://www.linkedin.com/in/marcilioduarte98/)
                [Github](https://github.com/marcilioduarte)
                @marcilioduarte | Economics and Data Science
                ''')
demo.launch()

2. Создайте свой файл requirements.txt следующим образом:

#requirements

pandas
plotly.express
scikit-learn
gradio

3. Создайте свой аккаунт здесь:

4. Создайте свое пространство:

5. Загрузите файл app.py и файл requirements.txt:

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

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

После этого наш проект завершен, и наше приложение работает на обнимающемся лице.

Чтобы получить к нему доступ, вы можете нажать на ссылку ниже:



Хотите вместе поработать над проектом или заинтересоваться моими услугами? Связаться со мной:

Линкедин

@marcilioduarte | Экономика и наука о данных | Гитхаб