Писать мою нейронную сеть против корпоративного эталона
- Введение
- Ключевые правила
- Тестовая метрика
- Поворот londingClub
- Моя очередь
- Победа!
Введение
В случае, если вы пропустили это, я построил нейронную сеть для прогнозирования риска кредита используя Общественный набор данных от LondingClub. . Тогда я построил Публичные API служить прогнозам модели. Это хорошее и все, но … как Хорошо моя модель?
Сегодня я собираюсь поставить его на тест, примет его к моделям риска самого института, который выдал эти кредиты. Это верно, LondingClub включал свои собственные расчетные оценки кредита (и подсерок) в набор данных, поэтому все предметы находятся на месте для самых захватывающих рисков, моделирующих моделей риска. век неделю. Пусть лучший алгоритм победит!
import joblib prev_notebook_folder = "../input/building-a-neural-network-to-predict-loan-risk/" loans = joblib.load(prev_notebook_folder + "loans_for_eval.joblib") loans.shape
(1110171, 70)
loans.head()
… | 5.91 | 36 месяцев | C | DEC-2015. | консолидация долгов | 3600.0 | C4. | 0.0 | 0 | 55000.0 | 10+ лет | 0.0 | 675.0 | ИПОТЕКА | 2400.0 | 4429.08 | 178050.0 | 148.0 | 7746.0 | 1.0 | 13734.0 |
… | 16.06 | 36 месяцев | C | DEC-2015. | малый бизнес | 24700.0 | C1. | 0.0 | 1 | 65000.0 | 10+ лет | 1.0 | 715.0 | ИПОТЕКА | 79300.0 | 29530.08 | 314017.0 | 192.0 | 39475.0 | 1.0 | 24667.0 |
… | 10.78 | 60 месяцев | B | DEC-2015. | home_improvement. | 20000.0 | B4. | 0.0 | 2 | 63000.0 | 10+ лет | 0.0 | 695.0 | ИПОТЕКА | 6200.0 | 25959.60 | 218418.0 | 184.0 | 18696.0 | 1.0 | 14877.0 |
… | 25.37 | 60 месяцев | F | DEC-2015. | major_purchase. | 10400.0 | F1. | 0.0 | 4 | 104433.0 | 3 года | 1.0 | 695.0 | ИПОТЕКА | 20300.0 | 17394.60 | 439570.0 | 210.0 | 95768.0 | 1.0 | 88097.0 |
… | 10.20 | 36 месяцев | C | DEC-2015. | консолидация долгов | 11950.0 | C3. | 0.0 | 5 | 34000.0 | 4 года | 0.0 | 690.0 | АРЕНДА | 9400.0 | 14586.48 | 16900.0 | 338.0 | 12798.0 | 1.0 | 4000.0 |
5 rows × 70 columns
Этот пост был адаптирован из ноутбука Jupyter, кстати, так что если вы хотите следовать в своем собственном ноутбуке, идите вперед и вилку Kaggle или Github Действительно
Ключевые правила
Это будет чистая бой – моя модель не будет использовать какие-либо данные LondingClub, не имел доступа к тому, что они рассчитывают оценку кредита (включая саму оценку).
Я собираюсь сортировать набор данных хронологически (используя Column espient_D
Столбец, месяц и год, который был выдан кредит) и разделил его на две части. Первые 80% я буду использовать для обучения моей модели конкуренции, и я сравним производительность на последних 20%.
from sklearn.model_selection import train_test_split loans["date"] = loans["issue_d"].astype("datetime64[ns]") loans.sort_values("date", axis="index", inplace=True, kind="mergesort") train, test = train_test_split(loans, test_size=0.2, shuffle=False) train, test = train.copy(), test.copy() print(f"The test set contains {len(test):,} loans.")
The test set contains 222,035 loans.
На более раннем конце тестового набора моя модель может оказать небольшое информационное преимущество, обученное на несколько кредитов, которые, возможно, не были закрыты, но в точке LondingClub оцениваем эти. С другой стороны, LondingClub может иметь небольшое информационное преимущество в более позднем конце тестового набора, поскольку они знают бы результаты некоторых кредитов на более раннем конце теста, установленного тем временем.
Я должен дать кредит Майкла Wurm, кстати, за идея По сравнению с производительностью моей модели к оценкам кредитов LeondingClub, но мой подход довольно разный. Я не пытаюсь имитировать выступление инвестиционного портфеля; Я просто оцениваю, насколько хорошо мои прогнозы простого риска сравнивать.
Тестовая метрика
Тест: кто может выбрать лучший набор кредитов, оценившихся на основе независимой переменной от мой последний ноутбук Фракция ожидаемого кредита возврата, что потенциальный заемщик будет возвращен (что я разработал как Fraction_Recovered
).
LondingClub сделает плита сначала. Я пойму все свои кредиты из тестового набора, посчитайте их и рассчитать их среднее fraction_recovered
Отказ Это среднее значение будет метрикой, моя модель должна победить.
Тогда я буду тренировать свою модель на тренировке, используя то же самое Трубопровод и параметры Я поселился в моей последней ноутбуке. Как только он обучен, я буду использовать его, чтобы сделать прогнозы на тестовом наборе, а затем соберите количество главных прогнозов, равных количеству кредитов класса LondingClub. Наконец, я рассчитаю то же среднее значение fraction_recovered
На этом подмножестве, и мы будем победителем!
Поворот londingClub
from statistics import mean lc_grade_a = test[test["grade"] == "A"] print(f"LendingClub gave {len(lc_grade_a):,} loans in the test set an A grade.") print("\nAverage `fraction_recovered` on LendingClub's grade A loans:") print(round(mean(lc_grade_a["fraction_recovered"]), 5))
LendingClub gave 38,779 loans in the test set an A grade. Average `fraction_recovered` on LendingClub's grade A loans: 0.96021
Это довольно высокий процент. Я немного нервничаю.
Моя очередь
Во-первых, я скопирую за мой Run_Pipeline
Функция из Мой предыдущий ноутбук :
from sklearn.model_selection import train_test_split from sklearn_pandas import DataFrameMapper from sklearn.preprocessing import OneHotEncoder, OrdinalEncoder, StandardScaler from tensorflow.keras import Sequential, Input from tensorflow.keras.layers import Dense, Dropout def run_pipeline( data, onehot_cols, ordinal_cols, batch_size, validate=True, ): X = data.drop(columns=["fraction_recovered"]) y = data["fraction_recovered"] X_train, X_valid, y_train, y_valid = ( train_test_split(X, y, test_size=0.2, random_state=0) if validate else (X, None, y, None) ) transformer = DataFrameMapper( [ (onehot_cols, OneHotEncoder(drop="if_binary")), ( list(ordinal_cols.keys()), OrdinalEncoder(categories=list(ordinal_cols.values())), ), ], default=StandardScaler(), ) X_train = transformer.fit_transform(X_train) X_valid = transformer.transform(X_valid) if validate else None input_nodes = X_train.shape[1] output_nodes = 1 model = Sequential() model.add(Input((input_nodes,))) model.add(Dense(64, activation="relu")) model.add(Dropout(0.3, seed=0)) model.add(Dense(32, activation="relu")) model.add(Dropout(0.3, seed=1)) model.add(Dense(16, activation="relu")) model.add(Dropout(0.3, seed=2)) model.add(Dense(output_nodes)) model.compile(optimizer="adam", loss="mean_squared_logarithmic_error") history = model.fit( X_train, y_train, batch_size=batch_size, epochs=100, validation_data=(X_valid, y_valid) if validate else None, verbose=2, ) return history.history, model, transformer onehot_cols = ["term", "application_type", "home_ownership", "purpose"] ordinal_cols = { "emp_length": [ "< 1 year", "1 year", "2 years", "3 years", "4 years", "5 years", "6 years", "7 years", "8 years", "9 years", "10+ years", ] }
Теперь на момент истины:
# Train the model _, model, transformer = run_pipeline( train.drop(columns=["issue_d", "date", "grade", "sub_grade", "expected_return"]), onehot_cols, ordinal_cols, batch_size=128, validate=False, ) # Make predictions X_test = transformer.transform( test.drop( columns=[ "fraction_recovered", "issue_d", "date", "grade", "sub_grade", "expected_return", ] ) ) test["model_predictions"] = model.predict(X_test) # Gather top predictions test_sorted = test.sort_values("model_predictions", axis="index", ascending=False) ty_grade_a = test_sorted.iloc[0:len(lc_grade_a)] # Display results print("\nAverage `fraction_recovered` on Ty's grade A loans:") print(format(mean(ty_grade_a["fraction_recovered"]), ".5f"))
Epoch 1/100 6939/6939 - 13s - loss: 0.0249 Epoch 2/100 6939/6939 - 13s - loss: 0.0204 Epoch 3/100 6939/6939 - 13s - loss: 0.0202 Epoch 4/100 6939/6939 - 13s - loss: 0.0202 Epoch 5/100 6939/6939 - 13s - loss: 0.0202 Epoch 6/100 6939/6939 - 14s - loss: 0.0201 Epoch 7/100 6939/6939 - 14s - loss: 0.0201 Epoch 8/100 6939/6939 - 14s - loss: 0.0201 Epoch 9/100 6939/6939 - 13s - loss: 0.0201 Epoch 10/100 6939/6939 - 12s - loss: 0.0201 Epoch 11/100 6939/6939 - 13s - loss: 0.0201 Epoch 12/100 6939/6939 - 13s - loss: 0.0201 Epoch 13/100 6939/6939 - 13s - loss: 0.0201 Epoch 14/100 6939/6939 - 13s - loss: 0.0201 Epoch 15/100 6939/6939 - 12s - loss: 0.0201 Epoch 16/100 6939/6939 - 12s - loss: 0.0201 Epoch 17/100 6939/6939 - 13s - loss: 0.0200 Epoch 18/100 6939/6939 - 13s - loss: 0.0200 Epoch 19/100 6939/6939 - 13s - loss: 0.0200 Epoch 20/100 6939/6939 - 14s - loss: 0.0200 Epoch 21/100 6939/6939 - 13s - loss: 0.0200 Epoch 22/100 6939/6939 - 13s - loss: 0.0200 Epoch 23/100 6939/6939 - 12s - loss: 0.0200 Epoch 24/100 6939/6939 - 12s - loss: 0.0200 Epoch 25/100 6939/6939 - 12s - loss: 0.0200 Epoch 26/100 6939/6939 - 13s - loss: 0.0200 Epoch 27/100 6939/6939 - 13s - loss: 0.0200 Epoch 28/100 6939/6939 - 13s - loss: 0.0200 Epoch 29/100 6939/6939 - 13s - loss: 0.0200 Epoch 30/100 6939/6939 - 13s - loss: 0.0200 Epoch 31/100 6939/6939 - 15s - loss: 0.0200 Epoch 32/100 6939/6939 - 13s - loss: 0.0200 Epoch 33/100 6939/6939 - 12s - loss: 0.0200 Epoch 34/100 6939/6939 - 13s - loss: 0.0200 Epoch 35/100 6939/6939 - 13s - loss: 0.0200 Epoch 36/100 6939/6939 - 13s - loss: 0.0200 Epoch 37/100 6939/6939 - 13s - loss: 0.0200 Epoch 38/100 6939/6939 - 13s - loss: 0.0200 Epoch 39/100 6939/6939 - 13s - loss: 0.0200 Epoch 40/100 6939/6939 - 13s - loss: 0.0200 Epoch 41/100 6939/6939 - 13s - loss: 0.0200 Epoch 42/100 6939/6939 - 13s - loss: 0.0200 Epoch 43/100 6939/6939 - 14s - loss: 0.0200 Epoch 44/100 6939/6939 - 13s - loss: 0.0200 Epoch 45/100 6939/6939 - 13s - loss: 0.0200 Epoch 46/100 6939/6939 - 13s - loss: 0.0200 Epoch 47/100 6939/6939 - 13s - loss: 0.0200 Epoch 48/100 6939/6939 - 13s - loss: 0.0200 Epoch 49/100 6939/6939 - 13s - loss: 0.0200 Epoch 50/100 6939/6939 - 13s - loss: 0.0200 Epoch 51/100 6939/6939 - 13s - loss: 0.0200 Epoch 52/100 6939/6939 - 13s - loss: 0.0200 Epoch 53/100 6939/6939 - 13s - loss: 0.0200 Epoch 54/100 6939/6939 - 14s - loss: 0.0200 Epoch 55/100 6939/6939 - 14s - loss: 0.0200 Epoch 56/100 6939/6939 - 13s - loss: 0.0200 Epoch 57/100 6939/6939 - 13s - loss: 0.0200 Epoch 58/100 6939/6939 - 13s - loss: 0.0200 Epoch 59/100 6939/6939 - 13s - loss: 0.0200 Epoch 60/100 6939/6939 - 13s - loss: 0.0200 Epoch 61/100 6939/6939 - 13s - loss: 0.0200 Epoch 62/100 6939/6939 - 13s - loss: 0.0200 Epoch 63/100 6939/6939 - 13s - loss: 0.0200 Epoch 64/100 6939/6939 - 13s - loss: 0.0200 Epoch 65/100 6939/6939 - 12s - loss: 0.0200 Epoch 66/100 6939/6939 - 13s - loss: 0.0200 Epoch 67/100 6939/6939 - 14s - loss: 0.0200 Epoch 68/100 6939/6939 - 13s - loss: 0.0200 Epoch 69/100 6939/6939 - 13s - loss: 0.0200 Epoch 70/100 6939/6939 - 13s - loss: 0.0200 Epoch 71/100 6939/6939 - 13s - loss: 0.0200 Epoch 72/100 6939/6939 - 13s - loss: 0.0200 Epoch 73/100 6939/6939 - 13s - loss: 0.0200 Epoch 74/100 6939/6939 - 13s - loss: 0.0200 Epoch 75/100 6939/6939 - 13s - loss: 0.0200 Epoch 76/100 6939/6939 - 13s - loss: 0.0200 Epoch 77/100 6939/6939 - 13s - loss: 0.0200 Epoch 78/100 6939/6939 - 13s - loss: 0.0200 Epoch 79/100 6939/6939 - 14s - loss: 0.0200 Epoch 80/100 6939/6939 - 13s - loss: 0.0200 Epoch 81/100 6939/6939 - 13s - loss: 0.0200 Epoch 82/100 6939/6939 - 13s - loss: 0.0200 Epoch 83/100 6939/6939 - 13s - loss: 0.0200 Epoch 84/100 6939/6939 - 12s - loss: 0.0200 Epoch 85/100 6939/6939 - 13s - loss: 0.0200 Epoch 86/100 6939/6939 - 13s - loss: 0.0200 Epoch 87/100 6939/6939 - 13s - loss: 0.0200 Epoch 88/100 6939/6939 - 13s - loss: 0.0200 Epoch 89/100 6939/6939 - 13s - loss: 0.0200 Epoch 90/100 6939/6939 - 13s - loss: 0.0200 Epoch 91/100 6939/6939 - 14s - loss: 0.0200 Epoch 92/100 6939/6939 - 13s - loss: 0.0200 Epoch 93/100 6939/6939 - 13s - loss: 0.0200 Epoch 94/100 6939/6939 - 13s - loss: 0.0200 Epoch 95/100 6939/6939 - 13s - loss: 0.0200 Epoch 96/100 6939/6939 - 13s - loss: 0.0200 Epoch 97/100 6939/6939 - 13s - loss: 0.0200 Epoch 98/100 6939/6939 - 13s - loss: 0.0200 Epoch 99/100 6939/6939 - 13s - loss: 0.0200 Epoch 100/100 6939/6939 - 13s - loss: 0.0200 Average `fraction_recovered` on Ty's grade A loans: 0.96166
Победа!
Феве, это был близкий! Моя победа может быть слишком маленькой, чтобы быть статистически значимым, но эй, прохладно видеть, что я могу отставать от лучших и ярких и ярких.
То, что мне очень хотелось бы знать, теперь, какой количественный диапазон предполагаемого риска каждая класс кредитования и суб-класс соответствует, но похоже на Это запатентованная Отказ Кто-нибудь знает, если оценки кредитов обычно соответствуют определенным процентам, таких как сорта буквения в академических классах? Если нет, есть какие-либо идеи для лучших ориентиров, которые я мог бы использовать для оценки производительности моей модели? Идти вперед и звонить в обсуждение ниже.
Оригинал: “https://dev.to/tymick/can-i-grade-loans-better-than-lendingclub-19jd”