Вопрос или проблема
Я работаю над проектом, связанным с использованием моделей машинного обучения для классификации заданного медицинского датасета. Датасет, который вы можете найти здесь: https://zenodo.org/records/10885957, содержит данные о 10 000 пациентах. У каждого пациента есть 39 признаков, а целевая колонка бинарная: 1 означает послеоперационные осложнения, 0 означает отсутствие послеоперационных осложнений.
Первое, что мы сделали, это проверили наличие пропущенных значений или дублирующихся строк. В данном случае их нет. Затем мы проверили распределение данных: 75% находятся в классе 0, остальные 25% в классе 1. Датасет довольно несбалансирован, если это распределение верно, не так ли?
На данный момент мы не предприняли никаких действий по поводу несбалансированности классов.
После этого первого анализа мы сделали train_test_split с размером теста, равным 0.2. Датасет представляет собой различные виды признаков, поэтому, чтобы использовать стандартный масштаб, мы разделили признаки на две большие группы (как в обучающей, так и в тестовой выборках). Первая группа (та, на которую применяется масштаб) состоит из “непрерывных” признаков, таких как концентрация натрия или процент лейкоцитов и т. д. Во второй группе находятся признаки, такие как возраст, пол и другие данные из опросов и т. д.
Мы применили стандартный масштаб к обучающей выборке, используя .fit_transform, и к тестовой выборке, используя .transform.
X_train, X_test = train_test_split(dataset, test_size=0.3, random_state=1)
y_train = X_train['Complications']
y_test = X_test['Complications']
X_train = X_train.drop(['Complications'],axis=1)
X_test = X_test.drop(['Complications'],axis=1)
X_continous_tr = pd.concat([X_train.iloc[:,0:6].reset_index(drop=True),X_train.iloc[:,17:37].reset_index(drop=True)],axis=1)
X_discrete_tr = pd.concat([X_train.iloc[:,6:17].reset_index(drop=True),X_train.iloc[:,37:40].reset_index(drop=True)],axis=1)
X_continous_ts = pd.concat([X_test.iloc[:,0:6].reset_index(drop=True),X_test.iloc[:,17:37].reset_index(drop=True)],axis=1)
X_discrete_ts = pd.concat([X_test.iloc[:,6:17].reset_index(drop=True),X_test.iloc[:,37:40].reset_index(drop=True)],axis=1)
scaler = StandardScaler()
X_continous_tr_sc = scaler.fit_transform(X_continous_tr)
X_continous_tr_sc = pd.DataFrame(X_continous_tr_sc)
X_continous_ts_sc = scaler.transform(X_continous_ts)
X_continous_ts_sc = pd.DataFrame(X_continous_ts_sc)
X_train = pd.concat([X_continous_tr_sc.reset_index(drop=True),X_discrete_tr.reset_index(drop=True)],axis=1)
X_test = pd.concat([X_continous_ts_sc.reset_index(drop=True),X_discrete_ts.reset_index(drop=True)],axis=1)
X_train.columns = X_train.columns.astype(str)
X_test.columns = X_test.columns.astype(str)
На этом этапе есть ли возможность утечки данных?
Мы попытались использовать несколько ядров, чтобы увидеть, как модель будет вести себя на датасете.
Сначала мы попробовали линейное ядро:
svm = SVC(kernel="linear", C=0.001)
svm.fit(X_train, y_train)
y_pred = svm.predict(X_test)
p = svm.predict(X_train)
print("Точность на тесте (линейное ядро):", accuracy_score(y_test, y_pred))
print("Точность на обучении (линейное ядро):", accuracy_score(y_train, p))
print(classification_report(y_test, y_pred))
print(classification_report(y_train, p))
И результаты следующие:
Точность на тесте (линейное ядро): 0.977
Точность на обучении (линейное ядро): 0.9798571428571429
precision recall f1-score support
0 0.97 1.00 0.98 2245
1 1.00 0.91 0.95 755
accuracy 0.98 3000
macro avg 0.99 0.95 0.97 3000
weighted avg 0.98 0.98 0.98 3000
precision recall f1-score support
0 0.97 1.00 0.99 5325
1 1.00 0.92 0.96 1675
accuracy 0.98 7000
macro avg 0.99 0.96 0.97 7000
weighted avg 0.98 0.98 0.98 7000
Можно ли считать такой результат переобучением? Точность на обучающей выборке и на тестовой довольно высока и схожа, а также различные оценки высоки в обоих случаях.
Затем мы продолжили с другими типами ядер, получив идеальный результат 1.0 с ядром rbf.
Есть ли здесь что-то неправильное? Является ли датасет слишком легким для моделей? Или мы не видим чего-то важного?
ПРИМЕЧАНИЕ: Единственное ядро, которое дает плохие результаты (57% точности), – это сигмоидное ядро.
Я надеюсь, что следующие советы помогут сделать вашу модель более общей:
- Используйте стратификацию:
X = dataset.drop(['Class'], axis=1)
y = dataset['Class']
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, test_size=0.3, random_state=1)
- Создайте валидационную выборку:
X_val, X_test, y_val, y_test = train_test_split(X_test, y_test, stratify=y_test, test_size=0.5, random_state=1)
Обучающая выборка (70%), Валидационная выборка (15%), Тест (15%)
- Используйте валидационную выборку для кросс-валидации:
Например: стратифицированное k-fold является хорошей отправной точкой.
Это основные вещи, которые помогают предотвратить переобучение.
Ответ или решение
Ваша работа над проектом на основе машинного обучения для классификации медицинского набора данных весьма обширна и требует внимания к различным аспектам, начиная с предварительного анализа и заканчивая оценкой модели. Давайте разберемся по шагам и ответим на все ваши вопросы.
1. Анализ данных
Вы правильно начали с проверки на пропуски и дубликаты в данных, а также с анализа распределения классов. Действительно, распределение, где 75% случаев относятся к классу 0 и 25% к классу 1, указывает на несбалансированность классов. Важно учитывать это в дальнейшем, так как несбалансированные данные могут привести к смещению модели в сторону более частого класса.
2. Разделение выборки
Вы правильно разделили данные на обучающую и тестовую выборки с использованием train_test_split
, однако рекомендуется применять стратификацию, чтобы сохранить истинное распределение классов в обеих выборках. Это можно сделать следующим образом:
from sklearn.model_selection import train_test_split
X = dataset.drop(['Complications'], axis=1)
y = dataset['Complications']
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, test_size=0.2, random_state=1)
3. Обработка признаков
Вы правильно разделили непрерывные и дискретные признаки и применили StandardScaler
только к непрерывным данным. Так как вы этого уже сделали, данная часть выглядит хорошо. Однако обратите внимание на то, что дискретные переменные могут нуждаться в кодировании (например, с помощью One-Hot Encoding) для более эффективного использования в модели SVM.
4. Возможные утечки данных
С точки зрения утечек данных, вы, кажется, избегаете этого, поскольку вы используете fit_transform
на обучающей выборке и transform
на тестовой. Это правильный подход, который минимизирует риск утечки информации.
5. Оценка модели и переобучение
Результаты, которые вы получили с линейным ядром (accuracy = 0.977 на тестовой выборке и 0.979 на обучающей), не обязательно указывают на переобучение, учитывая, что обе метрики близки друг к другу и довольно высоки. Однако это не исключает возможность того, что модель может хорошо работать на этом конкретном наборе данных, но может не обобщаться на новых данных.
Если RBF ядро дало идеальный результат 1.0, это может свидетельствовать о переобучении, особенно если вы видите хорошую производительность только на обучающей выборке с использованием сложных моделей. Проверьте, как модель ведет себя на кросс-валидации, чтобы дополнительно оценить ее обобщающую способность.
6. Рекомендации по улучшению модели
Чтобы улучшить вашу модель и её обобщающую способность, вы можете использовать следующие методы:
- Стратифицированное разбиение – как уже упомянуто, старайтесь сохранять пропорцию классов.
- Создание валидационного набора – вам нужно выделить часть данных для валидации, чтобы настраивать гиперпараметры модели, не используя тестовые данные.
X_val, X_test, y_val, y_test = train_test_split(X_test, y_test, stratify=y_test, test_size=0.5, random_state=1)
-
Использование перекрестной проверки – используйте метод стратифицированного K-блочного кросс-валидации для оценки модели. Это обеспечит надежное понимание возможностей вашей модели.
-
Настройка гиперпараметров – попробуйте использовать
GridSearchCV
илиRandomizedSearchCV
для поиска наилучших гиперпараметров для модели SVM. -
Обработка несбалансированных классов – вы можете использовать такие методы, как случайное увеличение выборки (SMOTE) или применение алгоритмов с учетом весов классов, чтобы улучшить работу модели с неравномерным распределением классов.
-
Анализ важности признаков – это может помочь вам лучше понять, какие признаки наиболее влияют на предсказания модели, и возможно, устранить ненужные признаки.
Заключение
Подводя итог, вы на правильном пути, и ваша работа по подготовке данных и первоначальной оценке модели выглядит внушительно. Внимание к деталям, таким как обработка несбалансированных данных и пересечение валидационных данным, будет полезно для улучшения вашей модели. Не забывайте всегда проверять результаты на валидационном наборе, а не только на тестовом, чтобы избежать переобучения.