Объяснение прогнозирования цены
Мне нужно понятно объяснить целевую переменную, median absolute percentage error (MedianAPE) и прогнозирование цены. Я хочу включить некоторый код на Python и убедиться, что упомяну все необходимые названия столбцов и пути к изображениям, а также столбцы train/test. Здесь может эффективно работать CatBoost. Важно обсудить преобразование целевой переменной и рассмотреть логарифмирование цены, чтобы минимизировать относительные ошибки. Хотя MedianAPE не является дифференцируемой метрикой, задание базовой модели на CatBoost для числовых и категориальных данных может дать хорошие результаты. Также я могу изучить использование предварительно обученных моделей для получения эмбеддингов изображений.
Описание решения
Мне нужно представить табличный baseline и рассмотреть возможность интеграции доступных изображений в эту задачу. Поскольку задача непосредственно связана с фотографиями и моделью, следует подробно описать мой подход. Если точных данных для продолжения нет, можно объяснить, что предоставить точный числовой ответ без файлов невозможно, однако, вероятно, требуется скорее концептуальное решение. Было запрошено «объясни ответ», то есть нужно предоставить объяснение, а не только готовый код. Я должен описать план решения, включая формулу метрики, в которой выполняется деление на истинное значение y, и проверить, что код корректно вычисляет medianAPE.
План построения модели
Я думаю о том, как обработать датасет. Если обучающие данные охватывают период с января по ноябрь, их можно разделить по дате. Если столбца с датой нет, возможно, придется вывести порядок косвенно. Вероятно, это связано с соревнованием Kaggle/Avito, поэтому моими первыми шагами будут загрузка датасетов и проверка таких столбцов, как id и price_TARGET. При создании признаков следует рассмотреть преобразование пробега, обработку координат и работу с категориальными признаками и списками комплектации.
Настройка признаков и модели
Я описываю, как обработать множественные варианты выбора: создать бинарные индикаторы токенов для категорий оборудования и заполнить пропущенные категориальные значения значением "unknown". Для моделирования я буду использовать CatBoost, контролируя возможную утечку целевой переменной и решая, нужно ли исключать ID из признаков. В первую очередь следует использовать разделение по времени для последних 20%, применяя столбцы с датой, если они доступны. Если даты нет, для отладки можно отсортировать данные по ID, но это не является оптимальным решением. Для моделирования я настрою CatBoostRegressor, при необходимости рассматривая альтернативы вроде LightGBM или XGBoost.
Обработка категориальных признаков в CatBoost
Я рассматриваю, как CatBoost работает с категориальными столбцами в Pandas. Похоже, категориальные признаки не должны содержать NaN или значения типа float, поэтому их потребуется преобразовать в строки. Для числовых столбцов можно заполнить пропуски, поскольку CatBoost умеет их обрабатывать. Также необходимо убедиться, что строки со списками корректно разбираются. Целевая переменная должна быть положительной, поэтому я применю y_log = np.log1p(y) для обучения и затем использую np.expm1 для обратного преобразования прогнозов, не забывая ограничить значения снизу нулем.
Изучение улучшений калибровки
Я рассматриваю возможность использования глобальной мультипликативной калибровки на валидации, поскольку она может исправить смещение в логарифмической шкале. Также можно использовать Out-Of-Fold (OOF) прогнозы.
Вместо ориентации только на среднее можно исследовать квантили. Поскольку Median Absolute Percentage Error (Median APE) основана на медиане и меньше зависит от выбросов, вычисление этой метрики может быть более подходящим. Нужно сосредоточиться на оптимизации устойчивой медианной оценки с использованием валидации.
Проверка модели
Я рассматриваю использование нескольких seed и вычисление медианы или среднего прогнозов, но необходимо соблюдать осторожность с валидацией, особенно если для временных данных используется только одно разбиение. Возможная проблема CatBoost заключается в том, что его встроенная метрика не поддерживает пользовательскую MedianAPE. Можно определить eval_metric, но это необязательно. В качестве функции ошибки можно использовать MAE/RMSE в логарифмической шкале и выполнять early stopping по MAE в логарифмической шкале, поскольку ошибки в логарифме цены симметричнее, чем ошибки в исходной шкале. Однако нужно предоставить полный каркас кода, даже если пользователь явно его не запрашивал.
Подготовка кода модели
Я хочу начать с «Краткий ответ: ...», а затем описать шаги. Можно включить код, устойчивый к отсутствующим столбцам, но не стоит чрезмерно его усложнять. Важно, чтобы код корректно запускался.
Я импортирую необходимые библиотеки и проверю регистр столбца ID в sample submission. В обучающем датасете он может называться ID, id или иначе, поэтому следует использовать способ автоматического определения.
Для цены я задам target='price_TARGET'. Также обработаю признаки и создам вспомогательную функцию для категориальных данных CatBoost, расширяя многозначные признаки в бинарные столбцы при необходимости.
Требования задачи
Я учитываю, что задача явно включает фотографии, поэтому сильное решение должно использовать и эту информацию. Можно привести псевдокод, не разрабатывая полный код на PyTorch для десятков или сотен тысяч фотографий. Важно объяснить baseline и возможные улучшения, но не быть чрезмерно многословным.
Настройка калибровки модели
Я сосредоточен на настройке калибровки модели с помощью диапазона c_values. Выбор значений на валидации имеет большое значение. Поскольку Median Absolute Percentage Error (APE) может лучше учитывать медианное масштабирование, нужно понять, как получить оптимальный коэффициент масштабирования. Перебор по сетке выглядит перспективным. Чтобы не допустить переобучения, следует использовать OOF/time folds и рассмотреть ансамблирование прогнозов нескольких seed. При формировании submission необходимо корректно сопоставить ID, сохранив исходный порядок и структуру столбцов. Также обязательно проверить пропущенные значения.
Проверка результатов модели
Нужно вычислить median APE и убедиться, что прогнозы положительны и конечны. Полезно построить baseline в виде медианной цены, сгруппированной по таким категориям, как марка и модель, и проверить его на временном разбиении. Сравнение модели с baseline имеет ключевое значение; следует изучить остатки и оценить качество на валидации. Также важно контролировать утечку данных: не использовать признаки, производные от цены, и не обучать кодировщики на полном размеченном датасете. Можно округлить широту и долготу, а также добавить геохэши для учета региональных особенностей, сохраняя строгое разделение данных.
Надежность кода
Я рассматриваю временную валидацию и структуру кода для табличного baseline. В фрагменте кода нужно обработать списки столбцов и сохранить читаемость, возможно, используя вспомогательную функцию для option_list. Также важно проверять NaN с помощью pd.isna. Я напишу надежную функцию для разбора вариантов, которая корректно работает с разными типами входных данных и возвращает очищенные, хорошо отформатированные значения.
Обработка типов данных для CatBoost
Необходимо правильно обработать категориальные данные. Если dtype равен object, можно использовать is_object_dtype и is_string_dtype. Проще определить категориальные столбцы следующим образом:
cat_cols = tr.select_dtypes(include=['object', 'category']).columns.tolist()
Нужно учитывать возможные значения None в списках и числовые значения с pd.NA, которые CatBoost может принять. Однако неясно, нужно ли преобразовывать их с помощью pd.to_numeric(). Пока можно оставить их без изменений.
Калибровка и методы валидации
Я думаю о минимизации log MAE и ее связи с медианой абсолютной логарифмической ошибки. Калибровочный коэффициент может помочь устранить смещение: умножение прогнозов на коэффициент бывает полезным.
Для центральных статистик можно использовать медианное отношение для калибровки. При кросс-валидации временные фолды защищают от временного сдвига, что особенно важно, если тестовые данные относятся к декабрю. Но если в данных нет даты, корректно разделить их невозможно; в таком случае остается полагаться на порядок строк, если он соответствует времени.
Работа с изображениями
Я рассматриваю способы получения дат, если они доступны; в противном случае случайные фолды могут использоваться только как приближение для анализа. Для архитектуры обработки изображений можно ограничиться максимум четырьмя изображениями на ID, используя строку ID. Для отсутствующих изображений потребуется использовать нулевые значения.
Эмбеддинги можно агрегировать с помощью среднего или максимума до фиксированного размера, дополнительно добавив количество изображений и индикатор отсутствия. Важно обучать PCA только на обучающей выборке, чтобы не использовать тестовую выборку при его настройке. Модель изображений также должна быть согласована с тем же временным разбиением.
Разбор данных
Я работаю с двумя датафреймами, df и other, и использую parse_options для обработки значений в определенном столбце. Цель состоит в создании новых столбцов на основе уникальных токенов с использованием префиксов, чтобы избежать дублирования. Ненужные столбцы из df следует удалить.
Функция parse_options должна корректно работать с разными типами данных, особенно при проверке пропусков. Сначала нужно отдельно обработать списки, чтобы избежать проблем при вызове pd.isna(v).
Согласование датафреймов
Необходимо убедиться, что Xtr и Xte содержат одинаковые столбцы. Если в Xte есть дополнительные столбцы, которых нет в Xtr, следует соблюдать осторожность: признаки, отсутствующие в обучении, не имеют смысла и могут быть заполнены NaN. Поэтому Xte нужно переиндексировать по столбцам Xtr:
Xte = Xte.reindex(columns=Xtr.columns)
Для категориальных столбцов следует определить их список, заполнить NaN значением "__NA__" и привести значения к строкам, чтобы CatBoost мог корректно с ними работать.
Интеграция изображений
Необязательно группировать медиану по бренду. Основная цель — корректно проверить ожидаемый score, не используя числовые значения из теста. Для интеграции изображений и CatBoost можно создать датафрейм признаков изображений и объединить его с табличными данными.
Если размер эмбеддингов равен 512, можно применить PCA и сократить его до 64 признаков. При этом необходимо сопоставлять ID, чтобы не допустить утечки во время валидации. Для ясности можно привести псевдокод, не добавляя конкретную реализацию на PyTorch. Также можно упомянуть названия предварительно обученных моделей.
Методы прогнозирования цены
Прямое прогнозирование цены отличается от использования логарифмического преобразования. Если применить CatBoost с loss_function='MAE' к логарифмированной целевой переменной, модель предсказывает медианную логарифмическую цену. После обратного преобразования получается оценка центрального значения в мультипликативной шкале, что примерно соответствует медианному прогнозу цены.
Объяснение основных принципов
Каждый компонент решения должен иметь понятное назначение. Например, отображение маршрутов важно для понимания соответствий путей, а использование id = filename.rsplit("_",1)[0] помогает корректно работать с именами файлов. Преобразование backbone в вектор является основой представления изображений. Агрегирование методами mean, max, first и count позволяет получить фиксированное представление данных. Обучение PCA уменьшает размерность, объединение по ID сохраняет целостность данных, а blending позволяет комбинировать разные прогнозы.
Проблемы в коде
Следует учитывать возможные проблемы при округлении широты и долготы, особенно если CatBoost получает категориальные признаки с высокой кардинальностью. Лучше сохранить исходные координаты как числовые признаки, а округленные значения использовать как категориальные бины. Односоставные русскоязычные названия полей не нужно подробно перечислять, поскольку они уже предоставлены пользователем.
Нужно решить, включать ли полноценный парсер многозначных признаков и запускаемый baseline. Я оформлю фрагменты кода понятно и отмечу необходимые корректировки, сохранив реализацию компактной.
Определение переменных для моделирования
Я задаю целевую переменную как "price_TARGET" и проверяю, присутствует ли в обучающем датасете столбец "ID", чтобы определить подходящее имя идентификатора. Если "ID" существует, я использую его, иначе применяю "id".
Эти настройки определяют основу последующих этапов построения модели, поэтому важно выбрать их корректно.
Работа с кодом CatBoost
Основной код CatBoost предполагает, что данные уже преобразованы, однако это может быть неудобно в образовательных целях. Хотя решение, вероятно, предназначено для соревнования, лучше включить компактную вспомогательную функцию.
Эта функция обрабатывает входные параметры, корректно работая с None, пустыми строками и некорректными значениями. Затем я создам дополнительные признаки в цикле, сформирую словарь допустимых значений и обработаю их соответствующим образом.
Согласование датафреймов
После согласования T должен содержать только столбцы из обучающего словаря. Это можно сделать следующим образом:
T = T.reindex(columns=X.columns)
Если в результате обработки многозначных полей появились новые столбцы, это допустимо при условии, что порядок признаков согласован. Также нужно проверить, что T не содержит исходных столбцов, отсутствующих в X. После этого следует преобразовать варианты выбора и в X, и в T, учитывая возможные преобразования дат и числовых признаков.
Метрики цены
Цены могут быть вещественными, поэтому их можно использовать в submission без округления. При вычислении абсолютных процентных ошибок не обязательно умножать значение на 100. Если score отображается в процентах, умножение на 100 следует выполнять только при выводе.
Например, score 0.17 означает, что типичная медианная относительная ошибка составляет 17%.
Затем baseline можно сравнить с медианным прогнозом цены, используя np.median. Если модель не улучшает baseline, необходимо изучить причины.
Выбор модели
CatBoost с нативной обработкой категориальных признаков предпочтительнее one-hot encoding. Поскольку каждой строке автомобиля соответствует уникальное имя файла изображения, использовать GroupKFold по ID необязательно.
Утечки через многозначные признаки нет, если они не содержат информацию о цене. Если используется ручное target encoding, его нужно обучать только на обучающих фолдах.
Также стоит рассмотреть признаки месяца и местоположения для учета временного сдвига. Финальный ответ следует начать с рекомендации по модели, а затем объяснить основные этапы решения.
Структура ответа
Я хочу создать структурированный ответ, начинающийся с краткого baseline. Затем следует привести подробную схему с нумерованными шагами.
Сначала нужно описать целевую функцию, где \( z = \log1p(\text{price}) \). Далее — этапы предобработки. После этого — значение фотографий. Затем следует обсудить валидацию и ансамблирование, а в конце — формирование submission и проверку результата.
Следует использовать естественный русский язык и оставить точные официальные названия, например medianAPE.
Краткий ответ: постройте ансамбль из CatBoost по табличным признакам и модели по эмбеддингам фотографий. Обучать лучше на \(\log(1+\text{price})\), а проверять качество именно через medianAPE. Разбиение для проверки должно быть временным: последние объявления из train имитируют декабрьский test.
1. Метрика и правильная проверка
import numpy as np
def median_ape(y_true, y_pred):
y_true = np.asarray(y_true, dtype=float)
y_pred = np.maximum(np.asarray(y_pred, dtype=float), 1e-6)
return np.median(np.abs(y_pred - y_true) / y_true)
Например, значение 0.18 означает 18%: у половины объектов относительная ошибка не больше 18%.
Случайное разбиение может дать слишком оптимистичный результат, потому что тест содержит объявления за декабрь 2024 года. Лучше:
- train: январь—октябрь;
- validation: ноябрь;
- финальное обучение: весь train, январь—ноябрь.
Если даты в parquet отсутствуют, случайное разбиение можно использовать только для предварительной проверки.
2. Обработка табличных данных
Категориальные признаки
Передайте в CatBoost как категории:
body_type;drive_type;engine_type;color;pts;steering_wheel;- одновыборные опции;
equipment.
Пропуски замените на отдельное значение, например "__NA__".
Мультивыборные признаки
Поле
["подогрев сидений", "климат-контроль"]
лучше преобразовать в несколько признаков:
obogrev_mult__подогрев сидений = 1
upravlenie_klimatom_mult__климат-контроль = 1
Дополнительно создайте число выбранных опций:
obogrev_mult__count = 2
Для [None] число опций равно нулю, а все бинарные признаки равны нулю.
Числовые признаки
Полезно добавить:
for df in (X, X_test):
if "mileage" in df.columns:
mileage = pd.to_numeric(df["mileage"], errors="coerce")
df["mileage_log"] = np.log1p(mileage.clip(lower=0))
df["mileage_missing"] = mileage.isna().astype("int8")
if "latitude" in df.columns:
df["latitude_round"] = df["latitude"].round(2).astype("string")
if "longitude" in df.columns:
df["longitude_round"] = df["longitude"].round(2).astype("string")
mileage_log помогает модели лучше работать с большим разбросом пробегов. Округлённые координаты позволяют учитывать регион продажи как категориальный признак.
3. Базовая модель CatBoost
import pandas as pd
import numpy as np
from catboost import CatBoostRegressor
train = pd.read_parquet("train_dataset.parquet")
test = pd.read_parquet("test_dataset.parquet")
TARGET = "price_TARGET"
# При необходимости укажите фактическое имя столбца ID
ID_COL = next(
c for c in train.columns
if c.lower() in {"id", "item_id", "ad_id"}
)
y = train[TARGET].astype(float).to_numpy()
X = train.drop(columns=[TARGET, ID_COL]).copy()
X_test = test.drop(columns=[ID_COL]).copy()
# После обработки мультивыборных полей
X_test = X_test.reindex(columns=X.columns)
cat_cols = X.select_dtypes(
include=["object", "category"]
).columns.tolist()
for c in cat_cols:
X[c] = X[c].fillna("__NA__").astype(str)
X_test[c] = X_test[c].fillna("__NA__").astype(str)
Разделение i_train и i_valid должно быть временным. Например, если строки уже упорядочены по дате:
cut = int(len(X) * 0.8)
i_train = np.arange(0, cut)
i_valid = np.arange(cut, len(X))
Если строки не упорядочены, нужно сначала отсортировать их по дате объявления.
Обучение:
y_log = np.log1p(y)
model = CatBoostRegressor(
iterations=3000,
depth=8,
learning_rate=0.03,
l2_leaf_reg=10,
loss_function="MAE",
eval_metric="MAE",
random_seed=42,
verbose=300
)
model.fit(
X.iloc[i_train],
y_log[i_train],
cat_features=cat_cols,
eval_set=(X.iloc[i_valid], y_log[i_valid]),
early_stopping_rounds=200
)
pred_valid = np.expm1(model.predict(X.iloc[i_valid]))
pred_valid = np.maximum(pred_valid, 1)
score = median_ape(y[i_valid], pred_valid)
print(f"medianAPE = {score:.4f} ({score * 100:.2f}%)")
Почему используется логарифм:
\[ z=\log(1+y). \]
Ошибка в 100 000 рублей для автомобиля за 500 000 рублей составляет 20%, а для автомобиля за 5 000 000 рублей — только 2%. Логарифм заставляет модель больше учитывать относительную, а не абсолютную ошибку.
Полезно сравнить два варианта:
- обучение на
y; - обучение на
log1p(y).
Оставьте тот, у которого меньше medianAPE на временной валидации.
4. Использование фотографий
Для каждого изображения:
100000_0.jpg
ID объявления равен части имени до последнего подчёркивания:
ad_id = filename.rsplit("_", 1)[0]
Для каждого изображения можно получить вектор признаков с помощью предварительно обученной сверточной модели. Затем объединить максимум четыре изображения одного автомобиля:
\[ F_i = [ \operatorname{mean}(e_{i1},\ldots,e_{ik}), \operatorname{max}(e_{i1},\ldots,e_{ik}), e_{i1}, k ]. \]
Где:
- \(e_{ij}\) — вектор признаков фотографии;
mean— средний вектор;max— покомпонентный максимум;- \(e_{i1}\) — признаки первой фотографии;
- \(k\) — число фотографий.
После этого:
- сопоставьте признаки с объявлениями по ID;
- уменьшите размерность PCA, например до 32–128 признаков;
- добавьте эти признаки к табличным;
- обучите CatBoost повторно.
PCA нужно обучать только на тренировочной части. Изображения лучше обрабатывать пакетами и сохранять готовые эмбеддинги, иначе повторное обучение будет медленным.
Фотографии можно использовать и отдельной моделью. Тогда на валидации подберите смешивание:
\[ z=\alpha z_{\text{table}}+(1-\alpha)z_{\text{image}}, \]
\[ \hat y=\exp(z)-1. \]
Проверяйте значения \(\alpha\) от 0 до 1 с шагом 0.05 и выбирайте то, при котором medianAPE минимален.
5. Финальное обучение и отправка
После выбора признаков и числа итераций обучите модель на всех 70 000 объектах:
final_model = CatBoostRegressor(
iterations=model.get_best_iteration() + 1,
depth=8,
learning_rate=0.03,
l2_leaf_reg=10,
loss_function="MAE",
random_seed=42,
verbose=300
)
final_model.fit(
X,
np.log1p(y),
cat_features=cat_cols
)
pred_test = np.expm1(final_model.predict(X_test))
pred_test = np.maximum(pred_test, 1)
Заполните шаблон sample_submission.csv, сохраняя его порядок:
sample = pd.read_csv("sample_submission.csv")
pred_by_id = pd.Series(
pred_test,
index=test[ID_COL].astype(str)
)
sample["target"] = (
sample["ID"]
.astype(str)
.map(pred_by_id)
)
assert sample["target"].notna().all()
assert np.isfinite(sample["target"]).all()
assert (sample["target"] > 0).all()
sample.to_csv("submission.csv", index=False)
В итоговом файле должны остаться ровно два столбца:
ID,target
Главная проверка перед отправкой:
print(sample.head())
print(sample.shape)
print(sample["target"].describe())
Если модель на временной валидации не превосходит простой прогноз медианной цены, сначала проверьте ID, обработку пропусков, мультивыборные поля и соответствие фотографий объявлениям.