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

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

Методы устранения недообучения

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
insurance = pd.read_csv('insurence.csv')

Создаем новые данные.

def preprocesing(df):

  df['sex'] = np.where(df.sex == 'male',1,0)
  df['smoker'] = np.where(df.smoker == 'yes',1,0)
  df = pd.get_dummies(df,prefix="",prefix_sep="")

  return df

insurance = preprocesing(insurance)

Мы создаем функцию для предварительной обработки данных.

def datatset(df):

  X = df.drop(columns=['charges']).values
  y = df.charges.values

  return X,y

X,y = datatset(insurance)

from sklearn.model_selection import train_test_split

X_train,X_test,y_train,y_test = train_test_split(X,
                                                 y,
                                                 test_size = 0.3,
                                                 random_state = 42)

Разделяем данные.

lm = LinearRegression(normalize=True).fit(X_train,y_train)

Мы предоставляем данные для обучения и проверки.

r2_train = lm.score(X_train,y_train)
r2_test = lm.score(X_test,y_test)

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

Создание новой переменной

fig = plt.subplots(1,1,figsize = (20,8))
_ = sns.scatterplot(data = insurance,
                      x = "age",
                       y = "charges",
                        hue = "smoker")

На графике представлены 4 типа кластеров:

  • Первый для здоровых людей, которые не курят, здоровы, как следствие, у них нет серьезных медицинских проблем. Люди, которые не курят, имеют серьезные проблемы со здоровьем.
  • Люди, которые курят, но имеют хорошее состояние здоровья.
  • Курильщики имеют серьезные проблемы со здоровьем.
  • Его можно упростить при двух условиях. Первый — когда состояние не столь серьезное, а второй — когда дело посвящено.

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

smoker_no_split = insurance.query("smoker == 'no'")
smoker_yes_split = insuarance.query("smoker == 'yes'")

Мы создаем два подмножества данных, чтобы облегчить обработку данных.

fig = plt.subplots(1,1,figsize = (20,8))
_ = sns.scatterplot(data = smoker_no_split,
                      x = "age",
                      y = "charges")

smoker_no_split['medical_problem'] = smoker_no_split["charges"] \ 
                                      .apply(lambda x: "severe" if x>17000 else "light")

Для тех значений, которые превышают 17 000 долларов США, это будет классифицировать их как серьезные медицинские проблемы.

fig = plt.subplots(1,1,figsize = (20,8))
_ = sns.scatterplot(data = smoker_yes_split,
                      x = "age",
                      y = "charges")

smoker_yes_split["medical_problem"] = smoker_yes_split["charges"] \ 
apply(lambda x: "severe" if x > 32000 else "light")

Из диапазона выше 32 000 долларов США мы могли бы создать новую группу аналогично некурящим, но с другим диапазоном.

smoker_yes_split["charges"]=smoker_yes_split["charges"] \ 
.apply(lambda x: 48000 if x > 48000 else x)

Убираем лишнее.

insurance_clear = pd.concat([smoker_no_split,smoker_yes_split])

Мы создаем фрейм данных с уже очищенными данными.

insurance_clear = preprocesing(insurance_clear)
insurance_clear.medical_problem = np.where(insurance_clear.medical_problem=="severe",1,0)

Снова проводим предварительную обработку данных.

X,y = datatset(insurance_clear)

X_train,X_test,y_train,y_test = train_test_split(X,
                                                 y,
                                                 test_size = 0.3,
                                                 random_state = 42)

Опять разделяем данные уже с чистыми данными.

lm = LinearRegression(normalize=True).fit(X_train,y_train)

Опять же, мы даем вам тренировочные данные.

Оценка

from sklearn.model_selection import cross_val_score

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

r2_train = lm.score(X_train,y_train)
r2_test = lm.score(X_test,y_test)
cv = cross_val_score(lm,X_test,y_test,cv = 10).mean()

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

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

Методы устранения переобучения

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np

Удалить выбросы

ford = pd.read_csv('ford.csv')

Загружаем данные.

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

Нет логического объяснения, где цена Форд Мустанг 2020 года с пробегом 50 миль меньше, чем спецверсия Форд Фокус, ее лучше исключить, так как она может перекосить модель.

ford_clear = ford.query('year<=2024 and price<=48_000')

Мы удаляем выбросы и выбираем автомобили по цене ниже 48 тысяч фунтов стерлингов.

plt.style.use('ggplot')

fig,ax = plt.subplots(1,2,figsize = (15,5))
ford.plot(kind = "scatter",
        x = "year",
        y = "price",
        ax = ax[0],
        title = "Diry Data")

ford_clear.plot(kind = "scatter",
        x = "year",
        y = "price",
        ax = ax[1],
         title = "Clear Data")

plt.savefig('remove_otlires.png')
plt.show()

Уже при устранении нетипичного значения наблюдается лучшее распределение данных.

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

X = ford_clear.drop(columns = ['price'])
X_ohe = pd.get_dummies(X,prefix_sep="",prefix="").values
y = ford_clear.price.values

from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X_ohe,
                                            y,
                                    test_size=0.3,
                                  random_state=42)

Мы разделяем переменные-предикторы и переменные a-предикторы.

from sklearn.linear_model import LinearRegression
lm = LinearRegression(normalize=True).fit(X_train,y_train)

Мы передаем данные обучения нашей линейной модели.

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

Преобразование переменной

ford_clear.mileage = ford_clear.mileage.apply(np.sqrt)
ford_clear.price = ford_clear.price.apply(np.log)

Логарифмические преобразования и извлечение квадратного корня из данных могут сгладить выбросы, поскольку они распределяют данные по значениям, более близким к среднему значению.

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

X = ford_clear.drop(columns = ['price'])
X_ohe = pd.get_dummies(X,prefix_sep="",prefix="").values
y = ford_clear.price.values

X_train,X_test,y_train,y_test = train_test_split(X_ohe,
                                  y,
                              test_size=0.3,
                                random_state=42)

Опять разделяем данные.

Оценка модели

lm = LinearRegression().fit(X_train,y_train)

Мы загружаем преобразованные данные в новую модель.

r2_train = lm.score(X_train,y_train)
r2_test = lm.score(X_test,y_test)
cv = cross_val_score(lm,X_test,y_test,cv = 5).mean()

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

test = pd.DataFrame()
test['true_values'] = np.exp(y_test)
test['pred_values'] = np.exp(lm.predict(X_test))


test.plot(kind = "scatter",
          x = "true_values",
          y = "pred_values",
          figsize = (15,5),
          title = "True Values vs Predicted Values",
          label = "Predicted Values")

plt.plot(test.true_values,test.true_values,
         label = "True Values")

plt.legend()
plt.savefig('pred_values_true_values.png')
plt.show()

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

Количество оценщиков

Этот метод можно использовать для всех алгоритмов, принадлежащих к семейству деревьев решений (например, Random Forest, XGBoost, Gradient Boosting, CatBoost, LightGBM и т. д.), для оценки поведения функции потерь для обучающих данных и проверки.

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

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

Мы загружаем данные после выполнения очистки данных.

def dataset():
    return df.drop(["charges"],axis="columns"),df.charges.values

X,y=dataset()


from sklearn.model_selection import train_test_split

X_train,X_test,Y_train,Y_test=train_test_split(X,
                                               y,
                                               test_size=0.33,
                                               random_state=42)

Мы разделяем данные обучения и проверки.

from sklearn.compose import make_column_transformer
from sklearn.pipeline import Pipeline


tf_colummns=make_column_transformer((MinMaxScaler(),["age","bmi","children"]), 
                                   (OneHotEncoder(drop="if_binary"),["region","sex",
                                                                     "smoker",
                                    "medical_problem"])) 

Мы применяем модель конвейера для облегчения предварительной обработки данных.

from sklearn.ensemble import GradientBoostingRegressor

def evaluate(max_depth,lr):
    
    estimator_list=[]
    mse_train_list=[]
    mse_test_list=[]

    estimators=np.arange(100,1000,step=2)
    for estimator in estimators:
      
        model=GradientBoostingRegressor(max_depth=max_depth,n_estimators=estimator,learning_rate=lr,random_state=42)
        model=pipeline_model(model)
        model.fit(X_train,Y_train)
        pred_train=model.predict(X_train)
        pred_test=model.predict(X_test)
        
        mse_train=mean_squared_error(Y_train,pred_train)
        mse_test=mean_squared_error(Y_test,pred_test)
        
        estimator_list.append(estimator)
        mse_test_list.append(mse_test)
        mse_train_list.append(mse_train)

        
    return estimator_list,mse_test_list,mse_train_list


def dataframe_evaluate_trees(max_depth,lr):
  
    n_trees,mse_test,mse_train=evaluate(max_depth=max_depth,lr=lr)

    df_evaluate=pd.DataFrame({"n_trees": n_trees,
                              "mse_test":mse_test,
                              "mse_train":mse_train}) 
  
    return df_evaluate


n_estimators_df = dataframe_evaluate_trees(max_depth=4,lr=0.01)
  • В этом случае мы используем модель Gradient Boosting.
  • Мы создаем функцию для хранения функции потерь в списках для данных обучения и проверки, так как в наборе данных очень мало данных, мы можем позволить использовать больше оценок, этот метод получит в качестве параметров максимальную глубину дерева и кубок обучения.
  • Мы создаем фрейм данных, в котором хранятся показатели производительности, чтобы позже построить график.
fig,ax = plt.subplots(1,1,figsize =(15,5)

ax.set_title('Max depth 4')

n_estimators_df.plot(
              kind = "scatter",
              x = "n_trees",
              y = "mse_train",
              label = "Train MSE",
              ax = ax)

n_estimators_df.plot(
              kind = "scatter",
              x = "n_trees",
              y = "mse_test",
              label = "Test MSE",
              ax = ax)

plt.savefig('n_estimators.png')
plt.show()

Генерируем график.

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

Глубокое обучение

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

import tensorflow as tf

Сетевая архитектура

model = tf.keras.Sequential([
    
    tf.keras.layers.Dense(input_dim = 9,units = 128,activation = "relu"),
    tf.keras.layers.Dense(units = 64,activation = "relu"),
    tf.keras.layers.Dense(units = 64,activation = "relu"),
    tf.keras.layers.Dense(units = 1,activation = "sigmoid")

])
  • Начальный слой получит 9 входных переменных, так как это количество независимых переменных, и он будет иметь функцию активации типа ReLU, поскольку она наиболее часто используется.
  • Сеть имеет два скрытых слоя с 64 нейронами с функцией активации ReLU.
  • Наконец, в выходном слое мы будем использовать функцию сигмода, так как она используется для двоичной классификации.
model.compile(loss = "bce",optimizer = "adam",metrics = "acc")
  • Мы назначаем функцию бинарной перекрестной энтропии, так как это проблема бинарной классификации.
  • Используемый оптимизатор будет Адам, так как он обычно дает очень хорошие результаты.
  • Выбранная метрика будет точностью.
history = model.fit(X_train_scaler,
                    y_train,
                    batch_size = 32,
                    epochs=100,
                    validation_data=(X_test_scaler,y_test),
                    validation_batch_size = 16)

Начнем с выполнения нейронной сети.

plt.style.use('ggplot')

def history_plot(history_model):
  fig,ax = plt.subplots(1,1,figsize = (15,5))
  ax.set_title('Loss Function')
  ax.plot(history_model.history['acc'],label = "Train Loss")
  ax.plot(history_model.history['val_acc'],label = "Test Loss")
  ax.legend()
  plt.savefig('loss_function.png')

Мы создаем график, чтобы показать поведение обучения модели.

Имеются явные признаки переобучения, поскольку точность обучающих данных значительно возрастает, а проверочных данных она сильно отстает.

Техники

Методы регуляризации

  • L1. Сведите к минимуму влияние переменных, которые не так сильно влияют, но добавляют что-то ценное. Он используется, когда мы знаем, что все независимые переменные служат для оценки.
  • L2:метод L2 присваивает ноль переменным, которые не лишены важности, он используется, когда мы не знаем, какие переменные могут повысить ценность модели.
  • L1_L2: объединяет методы регуляризации данных.

Друпю

tf.keras.layers.Dropout(rate = 0.3)
  • скорость:процент деактивированных нейронов.

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

Ранняя остановка

from tensorflow.keras.callbacks import EarlyStopping
early_stop = EarlyStopping(patience=3,
                          restore_best_weights=True)
                              
  • терпение: это означает, что если в итерациях функция стоимости для данных проверки не уменьшается, она остановится с обучением модели.
  • restore_best_weights: когда модель сбрасывает наилучшие предлагаемые веса сети.

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

Вторая модель

from tensorflow.keras.regularizers import L1L2
from tensorflow.keras.callbacks import EarlyStopping

model = tf.keras.Sequential([
    
    tf.keras.layers.Dense(input_dim = 9,units = 128,activation = "relu",kernel_regularizer = L1L2(0.001,0.001)),
    tf.keras.layers.Dense(units = 64,activation = "relu"),
    tf.keras.layers.Dense(units = 64,activation = "relu"),
    tf.keras.layers.Dropout(rate = 0.3),
    tf.keras.layers.Dense(units = 1,activation = "sigmoid")

])

model.compile(loss = "bce",optimizer = "adam",metrics = "acc")

Мы добавляем все эти стратегии для борьбы с переоснащением.

early_stop = EarlyStopping(patience=3,
                      restore_best_weights=True,
                          monitor='val_loss')

history = model.fit(X_train_scaler,
                    y_train,
                    batch_size = 16,
                    epochs=64,
                    validation_data=(X_test_scaler,y_test),
                    validation_batch_size = 8,
                    callbacks = [early_stop])

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

Несбалансированный набор данных

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import warnings

Загружаем необходимые библиотеки.

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

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

plt.style.use('ggplot')
fig,ax = plt.subplots(1,1,figsize = (15,5))
ax.set_title('Unbalanced Dataset')
sns.countplot(data = df,x = "Exited",ax = ax)
plt.savefig('unbalanced_dataset.png')
plt.show()

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

Первая модель

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

from sklearn.ensemble import RandomForestClassifier

clf = RandomForestClassifier(max_depth=6,
                             n_estimators = 400,
                             random_state = 42)
clf.fit(X_train,y_train)

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

  • max_depth: максимальная глубина каждого дерева.
  • n_estimators: количество примененных деревьев решений.
  • random_state: случайное состояние модели.
from sklearn.metrics import confusion_matrix

def cm_plot(cm):
    
    fig,ax = plt.subplots(1,1,figsize = (15,5))
    ax.set_title('Confussion Matrix')
    sns.heatmap(cm,annot=True,fmt=".1f",ax = ax,cmap="crest")

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

pred = clf.predict(X_test)
cm = confusion_matrix(y_test,pred)

cm_plot(cm)
plt.savefig('confusion_matrix_inbalaced.png')

Мы видим, что модель для класса 1 неправильно классифицирует большинство классов, что очень вредно, к счастью, есть очень эффективные методы решения этой проблемы.

Вторая модель

clf = RandomForestClassifier(max_depth=6,
                             n_estimators = 400,
                             random_state = 42,
                             class_weight="balanced")

clf.fit(X_train,y_train)

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

  • class_weight. Это относится к выравниванию весов для каждой категории, это очень помогает, когда класс чрезвычайно несбалансирован, как в этом случае.

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

УДАР

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

from imblearn.over_sampling import SMOTE

smote = SMOTE(random_state=42)

X_train_balanced,y_train_balanced = smote.fit_resample(X_train,y_train)
X_test_balanced,y_test_balanced = smote.fit_resample(X_test,y_test)

Генерируем уже сбалансированные данные.

clf = RandomForestClassifier(max_depth=6,
                             n_estimators = 400,
                             random_state = 42)

clf.fit(X_train_balanced,y_train_balanced)

Отдаем в модель уже сбалансированные данные.

pred = clf.predict(X_test_balanced)
cm = confusion_matrix(y_test_balanced,pred)
cm_plot(cm)
plt.savefig('smote_matrix.png')

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

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

Если вы хотите получить доступ к Github репозиторий, где будут блокноты, использованные при разработке статьи.