ВЫБОР ПРИЗНАКОВ В ПРОГНОЗИРОВАНИИ БАНКРОТСТВА

Теоретический раздел

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

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

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

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

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

В целом, MOEA предлагают мощный подход к выбору признаков, который может помочь принять обоснованные решения по очистке набора данных от недействительных и неважных переменных. Имеются работы, посвященные прогнозированию банкротства и выбору признаков по алгоритму МОЭА [1, 3, 4, 5].

В этом исследовании я буду использовать алгоритм NSGA-II (тип алгоритма MOEA) для выбора признаков. Мой выбор сделан в пользу этого алгоритма, так как работ, в которых он используется для выбора признаков банкротства, немного. Но есть много работ, где используется алгоритм NSGA-II для выбора признаков в разных областях [6, 7, 8, 9, 10].

Практический раздел

Мы будем работать с набором данных kaggle «Прогнозирование банкротства компаний» — «данные о банкротстве из Тайваньского экономического журнала за 1999–2009 годы».

Основная цель прогнозировать банкротство или небанкротство компании в будущем.

Итак, есть несколько способов реализовать подход MOEA к выбору функций в python. Существуют библиотеки pymoo, platypus, deap и др. для алгоритмов многокритериальной оптимизации [2]. Я использую pymoo для решения проблемы выбора функций. Ссылка в официальных документах здесь. Давайте посмотрим пример кода, и я объясню его шаг за шагом.

  • Прежде всего, мы импортируем библиотеки, загружаем и очищаем датасет финансовой отчетности компании.
from pymoo.core.problem import Problem
from pymoo.algorithms.moo.nsga2 import NSGA2
from pymoo.factory import get_sampling, get_crossover, get_mutation
from pymoo.optimize import minimize
from pymoo.operators.sampling.rnd import BinaryRandomSampling
from pymoo.operators.crossover.pntx import TwoPointCrossover
from pymoo.operators.mutation.bitflip import BitflipMutation

import pandas as pd
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
import plotly.express as px
import xgboost as xgb
from imblearn.over_sampling import SMOTE
from collections import Counter


df = pd.read_csv('data.csv')

X = df.drop(['Bankrupt?'], axis=1)
y = df[['Bankrupt?']]

print(X.shape[0])  # 96 ---> columns (independent variables)
  • Затем мы определяем количество значений целевой переменной.
df['Bankrupt?'].value_counts().plot(kind='bar')

"""
It's imbalanced class so that we see high disproportion between 0 ---> not bankruptcy and 1 ----> bankcruptcy
"""

  • Я использую XGBoost Classifier для прогнозирования банкротства.
X_train,X_test,y_train,y_test = train_test_split(X, y, test_size=0.33, random_state = 100)

smote = SMOTE(sampling_strategy='auto')

X_train_smote, y_train_smote = smote.fit_resample(X_train, y_train)

# train logistic regression model

clf = xgb.XGBClassifier()
clf.fit(X_train_smote, y_train_smote)

# compute accuracy and AUC on validation set

y_pred = clf.predict(X_test)
acc = accuracy_score(y_test, y_pred)
f_1 = f1_score(y_test, y_pred)


print('accuracy for XGBoost ----> ', acc) # accuracy for XGBoost ----> 0.9626832518880497
print('accuracy for f-1 score ----> ', f_1) # f-1 score for XGBoost ----> 0.46835443037974683
print(confusion_matrix(y_test, y_pred))

"""
Confusion matrix

[[2130 37] 
[ 47 37]]
"""
  • Мы видим высокую точность и низкий показатель f-1, что характерно для класса дисбаланса.
  • В матрице путаницы мы видим, что истинное предсказание 1 (банкротство) равно 37, а 2130 — для 0 (не банкротство).
  • Примечание! В прогнозировании банкротства мы должны ориентироваться на миноритарный класс (банкротство), поэтому важно прогнозировать именно банкротство компании.
  • Теперь я пытаюсь увеличить счет f-1, используя выбор функций.
  • Для выбора функций мы выбираем подход MOEA с использованием алгоритма pymoo и NSGA2.
  • Затем мы создаем класс BankruptcyProblem, который наследует класс Problem с основными параметрами и атрибутами:
# define the feature selecting problem
class BankruptcyProblem(Problem):

	def __init__(self, X, y):
	
		self.X = X
		self.y = y
		self.n_features = X.shape[1]
		self.eval_dict = {'accuracy':[], 'f-1 score':[]}
		super().__init__(n_var=self.n_features,
						n_obj=2,
						n_constr=0,
						xl=np.zeros(self.n_features),
						xu=np.ones(self.n_features),
						elementwise_evaluation=True,
						type_var=np.bool,
						)
  • Примечание! супер(). init позволяет вызвать конструктор родительского класса (Problem) внутри подкласса (BankruptcyProblem).
  • Задача — это объектно-ориентированное определение, которое реализует метод оценки набора решений. Для получения дополнительной информации вы можете написать эту и эту статьи.
# create the problem instance
problem = BankruptcyProblem(X.values, y.values)  # features -> cleaned and preprocessed finansial statments, target -> bankruptcy or not company
  • Мы используем алгоритм NSGA2 подхода MOEA.
# define the algorithm
algorithm = NSGA2(pop_size=1,
				  sampling=BinaryRandomSampling(),
				  crossover=TwoPointCrossover(),
				  mutation=BitflipMutation())
  • Функция _evaluate реализует алгоритм NSGA2 с набором данных финансовой отчетности и моделью XGBClassifier, которая представляет собой обучающую модель для прогнозирования банкротства компании, оценивает модель с использованием точности метрик и оценки f-1.
  • В строке out["F"] = [-acc, -f_1] мы сохраняем эти метрики для выбора наиболее важных признаков по алгоритму NSGA2.
def _evaluate(self, x, out, *args, **kwargs):

	# select features based on binary vector 
	selected_features = np.where(x == 1)[-1]
	X_selected = self.X[:, selected_features]
	
	# split data into train and validation sets
	X_train, X_valid, y_train, y_valid = train_test_split(X_selected, self.y, test_size=0.1, random_state=100)

	# use smote methodic for solving imbalanced data (disproportion between 0 and 1 values)
	smote = SMOTE(sampling_strategy='minority')
	X_train, y_train = smote.fit_resample(X_train, y_train)
	
	# train XGBClassifier model
	clf = xgb.XGBClassifier()
	clf.fit(X_train, y_train)
	
	# compute f_1 and AUC on validation set
	y_pred = clf.predict(X_valid)	
	acc = accuracy_score(y_valid, y_pred)	
	f_1 = f1_score(y_valid, y_pred)
	
	# save resiults
	self.eval_dict['accuracy'].append(acc)
	self.eval_dict['f-1 score'].append(f_1)
	
	# set objectives to be minimized
	out["F"] = [-acc, -f_1]
  • Наконец, запускаем алгоритм оптимизации, чтобы найти лучший набор классификаторов, который максимизирует несколько показателей производительности.
# run the optimization
res = minimize(problem,  # problem class
			   algorithm,  # NSGA2 algorithm
			   ("n_gen", 50), # number of iteration for eval problem class
			   verbose=True)
  • На рис. 1 показано, как изменить показатель f-1 на каждой итерации с помощью различных функций.

  • На рис. 2 показано, как изменить показатель точности на каждой итерации с разностными функциями.

  • Для извлечения важной функции мы используем объект res.
columns = X.columns.tolist()
columns_dict = {k:v for k, v in enumerate(columns)}  # create dict where key - index, value - columns name

# extract the selected features as index of dataset columns
selected_features = np.where(res.X == 1)[-1]

print("Selected features: ", selected_features)
"""

Selected features: [ 0 4 5 6 11 12 13 18 21 22 24 26 27 29 30 31 32 33 34 35 37 39 40 42 45 46 49 52 53 55 56 63 65 67 70 73 75 77 81 83 89 91 92 93] 

Reduced data dimension by 26 columns
 
"""
selected_features_final = [columns_dict[i] for i in selected_features]  # convert index to column name

print("Selected features final: ", selected_features_final)

"""
Selected features final: [' ROA(C) before interest and depreciation before interest', ' Realized Sales Gross Margin', ' Operating Profit Rate', ' Pre-tax net Interest Rate', ' Research and development expense rate', ' Cash flow rate', ' Interest-bearing debt interest rate', ' Persistent EPS in the Last Four Seasons', ' Operating Profit Per Share (Yuan ¥)', ' Per Share Net profit before tax (Yuan ¥)', ' Operating Profit Growth Rate', ' Regular Net Profit Growth Rate', ' Continuous Net Profit Growth Rate', ' Net Value Growth Rate', ' Total Asset Return Growth Rate Ratio', ' Cash Reinvestment %', ' Current Ratio', ' Quick Ratio', ' Interest Expense Ratio', ' Total debt[/Total](https://file+.vscode-resource.vscode-cdn.net/Total) net worth', ' Net worth[/Assets](https://file+.vscode-resource.vscode-cdn.net/Assets)', ' Borrowing dependency', ' Contingent liabilities[/Net](https://file+.vscode-resource.vscode-cdn.net/Net) worth', ' Net profit before tax[/Paid-in](https://file+.vscode-resource.vscode-cdn.net/Paid-in) capital', ' Accounts Receivable Turnover', ' Average Collection Days', ' Net Worth Turnover Rate (times)', ' Allocation rate per person', ' Working Capital to Total Assets', ' Current Assets[/Total](https://file+.vscode-resource.vscode-cdn.net/Total) Assets', ' Cash[/Total](https://file+.vscode-resource.vscode-cdn.net/Total) Assets', ' Current Liabilities[/Liability](https://file+.vscode-resource.vscode-cdn.net/Liability)', ' Current Liabilities[/Equity](https://file+.vscode-resource.vscode-cdn.net/Equity)', ' Retained Earnings to Total Assets', ' Current Asset Turnover Rate', ' Cash Turnover Rate', ' Fixed Assets to Assets', ' Current Liability to Equity', ' CFO to Assets', ' Current Liability to Current Assets', " Net Income to Stockholder's Equity", ' Degree of Financial Leverage (DFL)', ' Interest Coverage Ratio (Interest expense to EBIT)', ' Net Income Flag']

"""
  • Таким образом, мы определяем с помощью алгоритма NSGA2 важные функции. Теперь я фильтрую переменные X_train и X_test по важным функциям, снова подбираю классификатор XGBoost и сравниваю результаты.
X_train_new = X_train.iloc[:, selected_features]
X_test_new = X_test.iloc[:, selected_features]
y_train_new = y_train.copy()

smote = SMOTE(sampling_strategy='auto')

X_train_smote, y_train_smote = smote.fit_resample(X_train_new, y_train_new)

# train logistic regression model

clf = xgb.XGBClassifier()
clf.fit(X_train_smote, y_train_smote)

# compute accuracy and AUC on validation set

y_pred = clf.predict(X_test_new)
acc = accuracy_score(y_test, y_pred)
f_1 = f1_score(y_test, y_pred)

print('accuracy for XGBoost ----> ', acc) # accuracy for XGBoost ----> 0.9595735228787206
print('f-1 score for XGBoost ----> ', f_1)  # f-1 score for XGBoost ----> 0.49162011173184356
print(confusion_matrix(y_test, y_pred))

"""
Confusion matrix

[[2116 51] 
[ 40 44]]

"""
  • Результат с выбором характеристик орудия: показатель f-1 увеличился на 0,2 процента, но точность снизилась на 0,03 процента. Это не так уж много, но если мы посмотрим на матрицу путаницы, мы увидим, что истинное предсказание увеличения класса меньшинства в компании 7 по сравнению с классификатором XGBoost без выбора функций. Не забывайте, что всех компаний-банкротов 84, когда все тестовые данные 2251 и добиться результата по метрике f-1 score будет очень сложно из-за дисбаланса классов.
  • При всем при этом можно сделать вывод, что подгонка модели по данным с выделением признаков лучше предсказывает банкротство компании. Так же не забываем, что мы значительно уменьшили размерность датасета и это здорово скажется на скорости обучения модели, если в датасете много строк.

Примечание! Для достижения лучшего результата в прогнозировании банкротства компаний необходимо использовать не только отбор признаков, но и другие способы повышения качества модели. Например, нормализация и корреляция для обнаружения линейно-зависимых функций, GridSearchCV, RenadomSearchCV и байесовская оптимизация для поиска лучших гиперпараметров для модели и перекрестной проверки.

Объяснение алгоритма NSGA2

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

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

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

NSGA-II — это широко используемый алгоритм для задач многокритериальной оптимизации, который успешно применялся к различным реальным задачам, включая выбор признаков при прогнозировании банкротства.

Заключение

В заключение, выбор признаков является важным шагом в построении эффективных моделей прогнозирования банкротства. Выбор соответствующих функций может повысить точность модели, уменьшить переоснащение, повысить точность и показатели оценки f-1, а также улучшить ее интерпретируемость. В частности, было обнаружено, что методы многокритериального отбора признаков более эффективны при выборе наиболее информативных признаков для прогнозирования банкротства. Алгоритм NSGA-II — популярный метод многокритериальной оптимизации, который успешно применялся при выборе признаков для прогнозирования банкротства.

Рекомендации

  1. Выбор признаков для прогнозирования банкротства: многоцелевой подход к оптимизации
  2. Pymoo: многоцелевая оптимизация в Python
  3. Презентация Многоцелевая оптимизация
  4. Многоцелевые эволюционные алгоритмы выбора признаков: применение в прогнозировании банкротства
  5. Прогнозирование банкротства на основе несбалансированных данных с использованием многокритериального выбора классификаторов
  6. NSGA-II как метод выбора признаков и классификатор AdaBoost для прогнозирования COVID-19 на основе симптомов пациента
  7. Подход к выбору признаков на основе NSGA-II с ReliefF
  8. Многокритериальный выбор характеристик с использованием NSGA-II для прогнозирования оттока клиентов в телекоммуникациях
  9. Многокритериальный выбор признаков с помощью NSGA II для доменов распознавания образов
  10. Многокритериальная оценка генетического алгоритма при выборе признаков