Рассмотрение настройки модели CatBoost
Я думаю, что мне нужно включить полный базовый пример кода для использования CatBoost с текстовыми и image-эмбеддингами. Поскольку у нас нет фактического датасета, сложно привести числовые предсказания. Нужно подчеркнуть, что без файлов я не могу предоставить метрики.
Я опишу структуру: сначала кратко отвечу о обучении моделей с несколькими выходами и использовании различных типов эмбеддингов. Затем приведу основные шаги, возможно, даже некоторый код. Также следует объяснить используемую метрику.
Анализ признаков для модели
Я рассматриваю признаки, необходимые для модели. Для категориальных признаков можно использовать такие поля, как название категории, состояние товара, а также ID продавца или покупателя, особенно поскольку CatBoost умеет работать с высокой кардинальностью.
Для текстовых признаков у меня есть название и описание, но нужно внимательно настроить их конфигурацию, поскольку обработка текста в CatBoost может быть непростой. Можно объединить TF-IDF с Ridge или SGD либо использовать CatBoost.
Для изображений я рассмотрю pretrained-эмбеддинги, например ResNet или CLIP, а также добавлю признаки даты (например, год и месяц), цены и, возможно, размеры изображений.
Анализ именования и построения модели
Я рассматриваю соглашения об именовании модели, например target_weight и real_weight. Похоже, нужно учитывать столбцы в sample и не хардкодить значения. Я сопоставлю real_ с target_ с помощью словаря, чтобы сохранить правильный порядок. Также я хочу убедиться, что item_id сохраняется в файле submission, содержащем необходимые целевые столбцы. Судя по всему, включение изображений в модель является обязательным согласно требованиям задания для комплексного решения.
Предложение архитектуры мультимодальной модели
Я рассматриваю мультимодальную архитектуру, объединяющую текстовые, категориальные, числовые данные и изображения. Текст можно кодировать с помощью TF-IDF или pretrained мультиязычной модели, а CatBoost использовать для категориальных и числовых признаков. Для image-эмбеддингов можно задействовать pretrained-модели, такие как ResNet18 или EfficientNet. Следует включить код для базовых моделей и продемонстрировать извлечение признаков из изображений. Важно, чтобы реализация была технически надежной: нужно упомянуть загрузку весов, если это необходимо, и корректно сопоставить имена изображений с эмбеддингами. Наконец, следует объединить эти эмбеддинги с табличной моделью.
Проектирование пайплайна моделирования
Я рассматриваю, как создать чистую и надежную модель. Я понимаю, что CatBoost эффективно обрабатывает нелинейности и категориальные переменные, поэтому можно использовать CatBoostRegressor с title и description в качестве текстовых входов. Возможно, лучше сначала представить базовую модель, «вариант без фото», а затем показать улучшения от мультимодального подхода. Я хочу убедиться, что код можно запустить с изображениями, поэтому давайте спроектируем полный пайплайн.
Оценка конфигураций CatBoost
Я рассматриваю, насколько эффективно CatBoost обрабатывает seller_id и другие категориальные данные. Похоже, должен, но нужно убедиться, что во всех категориальных столбцах отсутствуют NaN, включая числовые признаки изображений и item_price, который я планирую заполнить медианой.
Я использую один и тот же split для всех целей. В предсказаниях применяется логарифмическое преобразование, и я проверяю возможные проблемы с пустыми текстовыми признаками в описаниях. Чтобы избежать проблем, можно просто использовать title и description, также при необходимости рассмотрев TF-IDF. В целом код должен работать.
Рассмотрение включения данных о фотографиях
Очевидно, что задание явно требует включить данные о фотографиях. Возможно, следует уточнить, что для первоначального рабочего baseline можно начать без изображений, а затем добавить их. Однако модель, игнорирующая изображения, может не полностью соответствовать требованиям. Поэтому разумно включить код для изображений, но упростить его и обозначить как optional, чтобы сохранить гибкость. Нужно выбрать лучший способ реализации!
Изучение возможностей CatBoostRegressor
Я рассматриваю CatBoostRegressor. Интересно, поддерживает ли он text_features вместе с loss_function=MAE. Оказывается, поддерживает! Однако я заметил, что в некоторых версиях указано: «Text features are not supported for GPU», поэтому следует использовать CPU. Установка task_type в GPU может привести к проблемам. Я планирую использовать depth=8 и 2000 итераций с early stopping. Также нужно объяснить сопоставление «real vs target» в тексте. Если фактических файлов нет, следует упомянуть, что проверить предсказания невозможно. Важно обеспечить ясность!
Уточнение процесса выполнения задания
Нужно учитывать, что пользователь упомянул: «Все данные можно скачать из Облака», но URL не предоставлен. Без данных я не могу провести анализ или предоставить score, поэтому следует сказать примерно следующее: «Без самого облака/файлов я не могу назвать score». Если пользователь спрашивает о фактической эффективности модели, придется подчеркнуть важность отсутствующих файлов. Задание не требует числового результата, поэтому финальный ответ следует структурировать ясно. Нужно дать краткий ответ, включающий подход, обоснование и рабочий baseline, не перегружая изложение.
Принятие решений по деталям кода
Я рассматриваю, не окажется ли полный код извлечения признаков из изображений слишком громоздким или отвлекающим. Вместо этого можно предоставить компактную optional-функцию, охватывающую все необходимые детали без излишнего усложнения. Важно убедиться, что нет скрытых проблем с политиками, и, к счастью, их нет. В финальном коде я использую markdown для ясности и не буду упоминать неподдерживаемый item_id как категориальный признак, одновременно объяснив его удаление. Так ответ останется сфокусированным и информативным!
Оценка эффективности модели
Я изучаю evaluation set и early stopping. Похоже, можно получить хороший score, и я рассчитываю macro score как среднее значение scores. Можно также рассмотреть optional-обработку случаев, когда обработка текста CatBoost завершается ошибкой, предложив TF-IDF в качестве альтернативы. Нужно убедиться, что фактический validation score корректно проверяется и подтверждается assertions в коде. Мы также можем сравнить его с baseline. Я позабочусь о том, чтобы четко объяснить все результаты!
Уточнение подхода к объяснению
Я думаю, действительно ли вопрос требует полного решения с кодом или достаточно концептуального ответа. Похоже, необходимо включить последнюю строку, чтобы после нее не было дополнительного текста. Я приведу пример, предлагающий более естественный следующий шаг.
В объяснении я начну с краткого ответа о решении задачи в виде четырех регрессий в log scale с необходимыми преобразованиями при обучении и инференсе. Уточню метрики, отмечу различающиеся шкалы целевых переменных и подчеркну важность конкретных ID в коде.
Краткий ответ
Задачу удобно решать как четыре отдельные регрессии:
- преобразовать каждый таргет:
\[ z=\log(1+y); \] - обучить модель на \(z\), используя категории, текст, цену, дату и признаки изображения;
- вернуть предсказание в исходную шкалу:
\[ \hat y=\exp(\hat z)-1; \] - посчитать среднее значение Log-MAE по четырём таргетам.
Так как метрика уже вычисляет MAE в логарифмической шкале, обучение на log1p(y) напрямую соответствует критерию соревнования.
Почему нужен логарифм
Для веса, высоты и длины значения могут сильно различаться. Ошибка в 1 кг для маленького товара гораздо существеннее, чем для большого.
Метрика использует:
\[ \left|\log(1+y)-\log(1+\hat y)\right|. \]
Поэтому вместо обучения на исходных значениях используем:
y_log = np.log1p(y)
После предсказания:
prediction = np.expm1(prediction_log)
prediction = np.clip(prediction, 0, None)
В обучающем файле таргеты называются real_..., а в отправке — target_..., поэтому нужно явно сопоставить их:
TARGET_MAP = {
"target_weight": "real_weight",
"target_height": "real_height",
"target_width": "real_width",
"target_length": "real_length",
}
Базовая модель
Для смешанных признаков удобно использовать CatBoostRegressor:
- категориальные признаки обрабатываются без ручного One-Hot Encoding;
- заголовок и описание можно передать как текстовые признаки;
- числовые признаки используются напрямую;
- для каждого таргета обучается отдельная модель.
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error
from catboost import CatBoostRegressor
Загрузка и подготовка данных
train = pd.read_parquet("train.parquet")
test = pd.read_parquet("test.parquet")
TARGET_MAP = {
"target_weight": "real_weight",
"target_height": "real_height",
"target_width": "real_width",
"target_length": "real_length",
}
TEXT_COLS = ["title", "description"]
CAT_COLS = [
"item_condition",
"category_name",
"subcategory_name",
"microcat_name",
"seller_id",
"buyer_id",
]
def make_features(df):
x = df.copy()
# Признаки даты
date = pd.to_datetime(x["order_date"], errors="coerce")
x["order_year"] = date.dt.year.fillna(0).astype("int16")
x["order_month"] = date.dt.month.fillna(0).astype("int8")
x["order_dayofweek"] = date.dt.dayofweek.fillna(0).astype("int8")
# Текстовые признаки
for col in TEXT_COLS:
x[col] = x[col].fillna("").astype(str)
x[col + "_len"] = x[col].str.len()
# Цена
x["item_price"] = (
pd.to_numeric(x["item_price"], errors="coerce")
.replace([np.inf, -np.inf], np.nan)
.fillna(0)
)
x["item_price_log"] = np.log1p(x["item_price"].clip(lower=0))
# Категории и ID превращаем в строки
for col in CAT_COLS:
x[col] = x[col].fillna("__NA__").astype(str)
# Это технические идентификаторы, а не содержательные признаки
x = x.drop(
columns=["item_id", "image_name", "order_date"],
errors="ignore"
)
return x
X = make_features(train)
X_test = make_features(test)
# На всякий случай выравниваем наборы признаков
X_test = X_test.reindex(columns=X.columns, fill_value=0)
Добавление признаков из фотографий
Имя файла фотографии само по себе почти ничего не говорит о товаре. Поэтому изображение нужно преобразовать в числовой вектор — эмбеддинг.
Например, можно использовать предобученную ResNet18:
import os
import torch
from pathlib import PurePath
from zipfile import ZipFile
from PIL import Image
from torchvision.models import resnet18, ResNet18_Weights
device = "cuda" if torch.cuda.is_available() else "cpu"
weights = ResNet18_Weights.DEFAULT
preprocess = weights.transforms()
image_model = resnet18(weights=weights)
image_model.fc = torch.nn.Identity()
image_model = image_model.to(device)
image_model.eval()
def get_image_embeddings(image_names, zip_path, batch_size=64):
image_names = [str(name) for name in image_names]
result = []
with ZipFile(zip_path) as archive:
archive_names = set(archive.namelist())
by_basename = {
os.path.basename(name): name
for name in archive_names
}
for start in range(0, len(image_names), batch_size):
batch_names = image_names[start:start + batch_size]
batch_images = []
for name in batch_names:
archive_name = name
if archive_name not in archive_names:
archive_name = by_basename.get(
os.path.basename(name)
)
if archive_name is None:
raise FileNotFoundError(name)
with archive.open(archive_name) as file:
image = Image.open(file).convert("RGB")
batch_images.append(preprocess(image))
batch = torch.stack(batch_images).to(device)
with torch.inference_mode():
embedding = image_model(batch)
result.append(embedding.cpu().numpy())
return np.vstack(result)
Извлекаем признаки для тренировочных и тестовых изображений:
image_train = get_image_embeddings(
train["image_name"],
"train.zip"
)
image_test = get_image_embeddings(
test["image_name"],
"test.zip"
)
Добавляем их к таблице:
for i in range(image_train.shape[1]):
X[f"image_{i}"] = image_train[:, i]
X_test[f"image_{i}"] = image_test[:, i]
Если сначала нужно проверить только таблично-текстовый baseline, этот блок можно временно пропустить.
Обучение четырёх моделей
Используем один и тот же разбор на обучение и валидацию для всех таргетов.
indices = np.arange(len(train))
idx_train, idx_valid = train_test_split(
indices,
test_size=0.2,
random_state=42
)
def create_model(iterations=2500):
return CatBoostRegressor(
iterations=iterations,
depth=8,
learning_rate=0.04,
loss_function="MAE",
eval_metric="MAE",
l2_leaf_reg=8,
random_seed=42,
allow_writing_files=False,
verbose=200
)
scores = {}
best_iterations = {}
for target_name, real_name in TARGET_MAP.items():
y = train[real_name].astype(float).to_numpy()
y_log = np.log1p(np.clip(y, 0, None))
model = create_model()
model.fit(
X.iloc[idx_train],
y_log[idx_train],
cat_features=CAT_COLS,
text_features=TEXT_COLS,
eval_set=(X.iloc[idx_valid], y_log[idx_valid]),
early_stopping_rounds=150
)
prediction_log = model.predict(X.iloc[idx_valid])
prediction = np.clip(np.expm1(prediction_log), 0, None)
score = mean_absolute_error(
y_log[idx_valid],
np.log1p(prediction)
)
scores[target_name] = score
best_iteration = model.get_best_iteration()
if best_iteration is None or best_iteration < 0:
best_iterations[target_name] = 2500
else:
best_iterations[target_name] = best_iteration + 1
print(f"{target_name}: {score:.5f}")
Итоговая метрика:
macro_log_mae = np.mean(list(scores.values()))
print("Macro Log-MAE:", macro_log_mae)
Именно это значение нужно использовать для проверки качества на локальной валидации.
Обучение на всех данных и создание отправки
submission = pd.read_csv("sample_submission.csv")
assert len(submission) == len(test)
for target_name, real_name in TARGET_MAP.items():
y = train[real_name].astype(float).to_numpy()
y_log = np.log1p(np.clip(y, 0, None))
final_model = create_model(
iterations=best_iterations[target_name]
)
final_model.fit(
X,
y_log,
cat_features=CAT_COLS,
text_features=TEXT_COLS,
verbose=False
)
prediction_log = final_model.predict(X_test)
prediction = np.clip(np.expm1(prediction_log), 0, None)
submission[target_name] = prediction
Проверяем результат и сохраняем файл:
prediction_columns = list(TARGET_MAP.keys())
assert np.isfinite(
submission[prediction_columns].to_numpy()
).all()
assert (
submission[prediction_columns].to_numpy() >= 0
).all()
submission.to_csv("submission.csv", index=False)
print(submission.head())
Главное
- Обучать нужно на
log1p(real_...). - Для каждого таргета нужна отдельная модель.
item_idиimage_nameне являются полезными числовыми признаками.- Фото сначала преобразуется в эмбеддинг.
- После предсказания применяется
expm1. - Итоговое качество — среднее из четырёх Log-MAE.
Без самих файлов данных нельзя назвать конкретный числовой score, но приведённый код вычислит его на валидационной выборке и создаст submission.csv.