Понимание наследования классов в приложениях Data Science и ML
В Python классы содержат атрибуты и методы. Атрибут — это переменная, которая хранит данные. Метод класса — это функция, принадлежащая классу, которая обычно выполняет некоторую логику над атрибутами класса.
Что такое наследование?
Наследование позволяет вам определить новый класс, который имеет доступ к методам и атрибутам другого класса, который уже был определен. Класс, у которого есть методы и атрибуты, которые будут унаследованы другим классом, называется родительским классом. Другие названия родительского класса, с которыми вы можете столкнуться, — это базовый класс и суперкласс. Класс, который имеет доступ к атрибутам и методам родительского класса, называется дочерним классом. Дочерний класс также называется подклассом. Помимо возможности определить класс, который наследуется от существующего класса, вы можете определить дочерний класс, который наследуется от нескольких родительских классов.
Что такое родительский класс?
Родительский класс имеет методы и атрибуты, которые наследуются новым дочерним классом. Родительские классы имеют методы и атрибуты, которые могут быть переопределены или настроены дочерним классом. Способ определить родительский класс в python — это просто определить класс с методами и атрибутами, точно так же, как вы обычно определяете обычный класс.
Ниже мы определяем простой пример родительского класса. Присутствует метод init, как и у обычного класса. В методе init мы определяем атрибут класса и сохраняем некоторое значение в атрибуте класса. Затем мы определяем метод класса с именем print_attribute, который печатает атрибут, определенный в методе init:
class ParentClass: def __init__(self, attribute): self.attribute = attribute def print_attribute(self): print(Self.attribute)
Что такое дочерний класс?
Дочерний класс наследует методы и атрибуты родительского класса. Другими именами дочернего класса являются подкласс и производный класс. Дочерний класс можно использовать для расширения функциональности родительского класса путем добавления новых методов и атрибутов. Его также можно использовать для переопределения или настройки родительского класса.
Дочерний класс определяется путем передачи родительского класса в качестве аргумента дочернему классу:
class ChildClass(ParentClass): def __init__(self): super().__iniit__(attribute) def print_attribute(self): print("attribute inherited from Parent Class:", self.attribute)
Каковы преимущества наследования?
Наследование очень мощно, поскольку позволяет разработчику ограничить дублирование кода. Разработав иерархию классов, вы можете предотвратить дублирование строк кода, выполняющих одну и ту же задачу. Это не только упрощает чтение кода, но и значительно повышает удобство сопровождения. Например, если в вашем коде есть много мест, где вы вычисляете частоту ошибок предсказаний модели, это можно преобразовать в метод родительского класса, который наследуется дочерними классами.
Когда иерархический дизайн классов (наследование) выполнен хорошо, это также облегчает тестирование и отладку. Это связано с тем, что четко определенные задачи будут локализованы в одном месте в базе кода, поэтому, когда необходимо внести изменения в способ выполнения задачи, поиск необходимого кода, который необходимо изменить, должен быть простым. Кроме того, после внесения изменения в метод родительского класса это изменение распространяется на все дочерние классы.
Науки о данных и машинного обучения
Многие пакеты, которые мы используем для машинного обучения, такие как Scikit-learn и Keras, содержат классы, которые наследуются от родительского класса. Например, такие классы, как линейная, регрессия, поддержка, вектор, машины и случайные леса, являются дочерними классами, которые наследуются от родительского класса с именем BaseEstimator. Базовый класс оценщика содержит такие методы, как прогнозирование и подгонка, с которыми должно быть знакомо большинство специалистов по данным.
Более интересным приложением является определение пользовательских дочерних классов, которые наследуются от родительских классов в Python, с помощью нескольких пакетов. Например, вы можете написать собственный класс dataframe в качестве дочернего класса, который наследуется от класса DataFrame в Pandas. Точно так же вы можете определить пользовательский класс классификации, который наследуется от родительского класса RandomForestClassifier.
Вы также можете определить собственные родительские и дочерние классы. Например, вы можете определить собственный родительский класс, который задает конкретный тип модели классификации, и дочерний класс, который анализирует выходные данные модели. Например, вы также можете написать родительский и дочерний классы для визуализации данных прогнозирования модели. Родительский класс может указывать атрибуты типа модели классификации и ее входных и выходных данных. Затем дочерний класс может генерировать визуализации, такие как матрицы путаницы, для анализа выходных данных модели.
Для наших моделей классификации мы будем работать с вымышленным набором данных Telco churn, который общедоступен на Kaggle. Набор данных можно использовать, изменять и распространять бесплатно в соответствии с Лицензией Apache 2.0.
Пример наследования в Python
Расширение существующего класса в пакете Python
Мы начнем с чтения наших данных об оттоке во фрейм данных Pandas:
import pandas as pd df = pd.read_csv('telco_churn.csv')
Далее, давайте определим наш ввод и вывод. Мы будем использовать поля MonthlyCharges, Gender, Tenure, InternetService и OnlineSecurity для прогнозирования оттока. Давайте преобразуем категориальные столбцы в машиночитаемые значения:
df['gender'] = df['gender'].astype('category') df['gender_cat'] = df['gender'].cat.codes df['InternetService'] = df['InternetService'].astype('category') df['InternetService_cat'] = df['InternetService'].cat.codes df['OnlineSecurity'] = df['OnlineSecurity'].astype('category') df['OnlineSecurity_cat'] = df['OnlineSecurity'].cat.codes df['Churn'] = np.where(df['Churn']=='Yes', 1, 0) cols = ['MonthlyCharges', 'tenure', 'gender_cat', 'InternetService_cat', 'OnlineSecurity_cat']
Далее, давайте определим наш ввод и вывод:
from sklearn.model_selection import train_test_split X = df[cols] y = df['Churn'] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
Теперь мы можем определить наш собственный дочерний класс. Давайте импортируем классификатор случайного леса из Scikit-learn и определим пустой класс с именем CustomClassifier. CustomClassifier примет в качестве аргумента класс RandomForestClassifier:
from sklearn.ensemble import RandomForestClassifier class CustomClassifier(RandomForestClassifier): pass
Мы укажем test_size в нашем методе инициализации, который позволяет нам указать размер наших тестовых и обучающих выборок. Мы также будем использовать метод super, чтобы позволить нашему пользовательскому классу наследовать методы и атрибуты класса случайного леса. Это расширит родительский класс случайного леса любыми дополнительными пользовательскими методами.
from sklearn.ensemble import RandomForestClassifier class CustomClassifier(RandomForestClassifier): def __init__(self, test_size=0.2, **kwargs): super().__init__(**kwargs) self.test_size = test_size
Теперь мы определим метод, который разделяет наши данные для обучения и тестирования:
from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split class CustomClassifier(RandomForestClassifier): def __init__(self, test_size=0.2, **kwargs): super().__init__(**kwargs) self.test_size = test_size def split_data(self): self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(X, y, test_size=self.custom_param, random_state=42)
Далее мы можем определить экземпляр нашего класса. Мы передадим значение 0,2 для нашего test_size. Это означает, что тестовый набор будет состоять из 20 процентов данных, а обучающий набор — из оставшихся 80 процентов:
rf_model = CustomClassifier(0.2) rf_model.split_data()
Мы можем понять наш дочерний класс, распечатав атрибуты и методы нашего дочернего класса:
print(dir(rf_model)) ['__abstractmethods__', '__annotations__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_abc_impl', '_check_feature_names', '_check_n_features', '_compute_oob_predictions', '_estimator_type', '_get_oob_predictions', '_get_param_names', '_get_tags', '_make_estimator', '_more_tags', '_repr_html_', '_repr_html_inner', '_repr_mimebundle_', '_required_parameters', '_set_oob_score_and_attributes', '_validate_X_predict', '_validate_data', '_validate_estimator', '_validate_y_class_weight', 'apply', 'base_estimator', 'bootstrap', 'ccp_alpha', 'class_weight', 'criterion', 'decision_path', 'estimator_params', 'feature_importances_', 'fit', 'get_params', 'max_depth', 'max_features', 'max_leaf_nodes', 'max_samples', 'min_impurity_decrease', 'min_samples_leaf', 'min_samples_split', 'min_weight_fraction_leaf', 'n_estimators', 'n_features_', 'n_jobs', 'oob_score', 'predict', 'predict_log_proba', 'predict_proba', 'random_state', 'score', 'set_params', 'split_data', 'test_size', 'verbose', 'warm_start']
Чтобы показать, что наш пользовательский экземпляр дочернего класса имеет доступ к методам и атрибутам родительского класса случайного леса, давайте попробуем подогнать модель случайного леса к нашим обучающим данным через наш собственный экземпляр класса:
rf_model = CustomClassifier(0.2) rf_model.split_data() rf_model.fit(rf_model.X_train, rf_model.y_train)
Этот код выполняется без ошибок, несмотря на то, что мы вызываем метод, принадлежащий внешнему классу. В этом вся прелесть наследования в Python! Это позволяет вам легко расширять функциональность существующих классов, независимо от того, являются ли они частью пакета или пользовательскими.
Мы также можем получить доступ к атрибутам класса классификатора случайного леса в дополнение к методам. Например, важность признаков является атрибутом класса классификатора случайного леса. Давайте получим доступ и отобразим важность функции случайного леса, используя экземпляр нашего класса:
importances = dict(zip(rf_model.feature_names_in_, rf_model.feature_importances_)) print("Feature Importances: ", importances)
Это дает следующие результаты:
Feature Importances: {'MonthlyCharges': 0.5192056776242303, 'tenure': 0.3435083140171441, 'gender_cat': 0.015069195786109523, 'InternetService_cat': 0.0457071535620191, 'OnlineSecurity_cat': 0.07650965901049701}
Расширение пользовательского родительского класса
Другой вариант использования машинного обучения для наследования Python — расширение функциональности пользовательского родительского класса с помощью дочернего класса. Например, мы можем определить родительский класс, который обучает модель случайного леса. Затем мы можем определить дочерний класс, который генерирует матрицу путаницы, используя наследуемый тестовый набор и атрибуты прогнозирования.
Начнем с определения класса, который мы будем использовать для построения нашей модели. В этом примере мы будем использовать библиотеку Seaborn и метод путаницы метрик из модуля metrics. Мы также будем хранить обучающие и тестовые наборы как атрибуты нашего родителя:
from sklearn.metrics import confusion_matrix import seaborn as sns class Model: def __init__(self): self.n_estimators = 10 self.max_depth = 10 self.y_test = y_test self.y_train = y_train self.X_train = X_train self.X_test = X_test
Затем мы можем определить метод подгонки, который сопоставляет классификатор случайного леса с нашими обучающими данными:
from sklearn.metrics import confusion_matrix import seaborn as sns class Model: ... def fit(self): self.model = RandomForestClassifier(n_estimators = self.n_estimators, max_depth = self.max_depth, random_state=42) self.model.fit(self.X_train, self.y_train)
Наконец, мы можем определить метод прогнозирования, который возвращает прогнозы модели:
from sklearn.metrics import confusion_matrix import seaborn as sns class Model: ... def predict(self): self.y_pred = self.model.predict(X_test) return self.y_pred
Теперь мы можем определить класс нашего ребенка. Давайте назовем наш дочерний класс «ModelVisulaization». Этот класс унаследует метод и атрибуты нашего класса Model:
class ModelVisualization(Model): def __init__(self): super().__init__()
Мы расширим класс нашей модели, добавив метод, генерирующий матрицу путаницы:
class ModelVisualization(Model): def __init__(self): super().__init__() def generate_confusion_matrix(self): cm = confusion_matrix(self.y_test, self.y_pred) cm = cm / cm.astype(np.float).sum(axis=1) sns.heatmap(cm, annot=True, cmap='Blues')
Теперь мы можем определить экземпляр нашего дочернего класса и построить нашу матрицу путаницы:
results = ModelVisualization() results.fit() results.predict() results.generate_confusion_matrix()
Это генерирует следующее:
Очевидно, что существует некоторая свобода в том, как вы проектируете пользовательские классы, независимо от того, являются ли они потомками классов в существующих пакетах или других пользовательских родительских классов. В зависимости от вашего варианта использования один маршрут может иметь больше смысла, чем другой. Например, если вы просто хотите добавить небольшое количество дополнительных атрибутов и методов, расширение существующего класса может оказаться более подходящим. Если необходимо настроить большое количество задач, более подходящим будет создание собственных родительских и дочерних классов.
Код из этого поста доступен на GitHub
Выводы
Наследование классов Python может быть чрезвычайно полезным для задач науки о данных и машинного обучения. Распространенным вариантом использования является расширение функциональных возможностей классов, являющихся частью существующих пакетов машинного обучения. Хотя мы рассмотрели расширение класса классификатора случайного леса здесь, вы также можете расширить функциональность класса фрейма данных Pandas и классов преобразования данных, таких как стандартный масштабатор и мин-макс масштабатор. Общее понимание того, как использовать наследование Python для расширения существующих классов, полезно для специалистов по данным и инженеров по машинному обучению.
Кроме того, бывают случаи, когда многие задачи, являющиеся частью отдельных рабочих процессов, необходимо настраивать. Мы рассмотрели пример расширения пользовательского класса, который мы использовали для построения модели классификации с функциональностью визуализации. Это позволяет нам наследовать методы и атрибуты класса моделирования и создавать визуализации в дочернем классе.
Это сообщение изначально было опубликовано во Встроенном блоге. Оригинал произведения можно найти здесь.
Подпишитесь на DDIntel Здесь.
Посетите наш сайт здесь: https://www.datadriveninvestor.com
Присоединяйтесь к нашей сети здесь: https://datadriveninvestor.com/collaborate