Bootstrap для 95% доверительного интервала
Вы хотите повторить анализ нескольких повторных выборок ваших данных. В общем случае предположим, что у вас есть функция f(x)
, которая определяет любую необходимую вам статистику на основе данных x
, и вы можете загрузить ее так:
def bootstrap(x, f, nsamples=1000):
stats = [f(x[np.random.randint(x.shape[0], size=x.shape[0])]) for _ in range(nsamples)]
return np.percentile(stats, (2.5, 97.5))
Это дает вам так называемые плагины для оценки 95% доверительного интервала (т. Е. Вы берете процентили распределения начальной загрузки).
В вашем случае вы можете написать более конкретную функцию, такую как
def bootstrap_auc(clf, X_train, y_train, X_test, y_test, nsamples=1000):
auc_values = []
for b in range(nsamples):
idx = np.random.randint(X_train.shape[0], size=X_train.shape[0])
clf.fit(X_train[idx], y_train[idx])
pred = clf.predict_proba(X_test)[:, 1]
roc_auc = roc_auc_score(y_test.ravel(), pred.ravel())
auc_values.append(roc_auc)
return np.percentile(auc_values, (2.5, 97.5))
Здесь clf
- это классификатор, для которого вы хотите проверить производительность, а X_train
, y_train
, X_test
, y_test
такие же, как в вашем коде.
Это дает мне следующие доверительные интервалы (округленные до трех цифр, 1000 образцов начальной загрузки):
- Наивный байесовский: 0,986 [0,980 0,988] (оценка, нижний и верхний пределы доверительного интервала)
- Случайный лес: 0,983 [0,974 0,989]
- Многослойный персептрон: 0,974 [0,223 0,98]
Тесты перестановки для проверки случайной производительности
Тест перестановки технически прошел бы по всем перестановкам вашей последовательности наблюдений и оценил бы вашу кривую roc с переставленными целевыми значениями (особенности не переставлены). Это нормально, если у вас есть несколько наблюдений, но это становится очень дорогостоящим, если вам нужно больше наблюдений. Поэтому принято подвыборывать количество перестановок и просто делать несколько случайных перестановок. Здесь реализация зависит в большей степени от конкретной вещи, которую вы хотите протестировать. Следующая функция делает это для ваших значений roc_auc
def permutation_test(clf, X_train, y_train, X_test, y_test, nsamples=1000):
idx1 = np.arange(X_train.shape[0])
idx2 = np.arange(X_test.shape[0])
auc_values = np.empty(nsamples)
for b in range(nsamples):
np.random.shuffle(idx1) # Shuffles in-place
np.random.shuffle(idx2)
clf.fit(X_train, y_train[idx1])
pred = clf.predict_proba(X_test)[:, 1]
roc_auc = roc_auc_score(y_test[idx2].ravel(), pred.ravel())
auc_values[b] = roc_auc
clf.fit(X_train, y_train)
pred = clf.predict_proba(X_test)[:, 1]
roc_auc = roc_auc_score(y_test.ravel(), pred.ravel())
return roc_auc, np.mean(auc_values >= roc_auc)
Эта функция снова принимает ваш классификатор как clf
и возвращает значение AUC для данных без перетасовки и значение p (т. Е. Вероятность наблюдать значение AUC, большее или равное значению, которое вы имеете в данных без перестановок).
Выполнение этого с 1000 выборками дает p-значения 0 для всех трех классификаторов. Обратите внимание, что они не являются точными из-за выборки, но они указывают на то, что все эти классификаторы работают лучше, чем случайные.
Тест перестановки для различий между классификаторами
Это намного проще. Учитывая два классификатора, у вас есть прогноз для каждого наблюдения. Вы просто перетасовываете распределение между предсказаниями и классификаторами, как это
def permutation_test_between_clfs(y_test, pred_proba_1, pred_proba_2, nsamples=1000):
auc_differences = []
auc1 = roc_auc_score(y_test.ravel(), pred_proba_1.ravel())
auc2 = roc_auc_score(y_test.ravel(), pred_proba_2.ravel())
observed_difference = auc1 - auc2
for _ in range(nsamples):
mask = np.random.randint(2, size=len(pred_proba_1.ravel()))
p1 = np.where(mask, pred_proba_1.ravel(), pred_proba_2.ravel())
p2 = np.where(mask, pred_proba_2.ravel(), pred_proba_1.ravel())
auc1 = roc_auc_score(y_test.ravel(), p1)
auc2 = roc_auc_score(y_test.ravel(), p2)
auc_differences(auc1 - auc2)
return observed_difference, np.mean(auc_differences >= observed_difference)
С помощью этого теста и 1000 образцов я не вижу существенных различий между тремя классификаторами:
- Наивный байесовский и случайный лес: diff = 0.0029, p (diff>) = 0.311
- Наивный Байес против MLP: diff = 0,0117, p (diff>) = 0,186
- случайный лес против MLP: diff = 0.0088, p (diff>) = 0.203
Где diff обозначает разницу в кривых roc между двумя классификаторами, а p (diff>) - эмпирическая вероятность наблюдать большую разницу в перемешанном наборе данных.