Вам предстоит работать над каталогом мобильных приложений RuStore. В день на модерацию попадают сотни новых приложений. Чтобы пользователи могли легко находить нужное, каждое приложение должно быть привязано к одной или нескольким категориям — например, finance, tools, children, arcade. Сейчас модераторы тратят часы, вручную читая описание и определяя, куда отнести каждое приложение. Это долго, скучно и дорого. Ваша задача — автоматизировать этот процесс: научиться по тексту описания приложения определять его категории.
Что дано
Вам предоставлены тексты приложений в двух вариантах:
full_description — полное описание приложения (если есть);
shortDescription — краткое описание (если есть).
app_name — название приложения;
labels_str — целевые категории, перечисленные через “|”.
Что нужно сделать
По app_name и описаниям для приложений из тестового набора предсказать до трёх категорий, упорядоченных по убыванию уверенности.
Формат ответа
Файл TSV с колонками:
app_name labels_str
Метрика
Hitrate@3 — если хотя бы одна правильная категория попала в топ-3 предсказаний, считаем это попаданием.
H@3 = (# приложений с попаданием) / (# всех приложений)
- Если приложение имеет 1 правильную метку, и она в топ-3 предсказаний → +1
- Если приложение имеет 2 правильные метки, и хотя бы одна в топ-3 → +1
- Усредняем по всем объектам теста.
Учебный ноутбук:
Это учебный ноутбук с кратким вводом в практику ML и базовым (бейзлайн) решением задания. Подходит для первого старта: используйте как отправную точку и улучшайте.
Система оценки
Количество баллов за задание определяется по формуле:
Points = 300 ⋅ percentileofscore(H@3s, H@3),
где
Points – количество баллов,
percentileofscore – функция, вычисляющая процентильное место участника,
H@3s – значения метрики H@3 всех участников,
H@3 – значение метрики данного участника.
Ниже — подробное решение и план действий для задачи автоматизации категоризации приложений RuStore по текстам описаний и названию. Я подаю как пошаговый учебный ноутбук с базовым бейзлайн-решением, которое подходит для средней школы и легко реализуется в Python с помощью scikit-learn.
Цель задачи
- По app_name, full_description, shortDescription определить до 3 целевых категорий (labels_str) в порядке убывания уверенности.
- Формат выхода: TSV файл с двумя столбцами: app_name labels_str
- Метрика для оценки: Hitrate@3 (H@3) — считается если хотя бы одна правильная метка попала в топ-3 предсказаний на уровне примера.
Резюме подхода (бейзлайн)
- Объединяем текстовые поля в один текстовый сабжект: текст = app_name + " " + full_description (если есть) + " " + shortDescription (если есть).
- Препроцессинг: приведение к нижнему регистру, базовая очистка, опционально удаление стоп-слов (для английского текста).
- Векторизация: TF-IDF представление текста.
- Модель: много-метокое решение на основе OneVsRestClassifier с логистической регрессией (LogisticRegression). Это позволяет получить вероятности принадлежности каждого класса.
- Препроцессинг меток: из labels_str формируем бинарный вектор целевых меток для обучения. Лейблы — множество категорий из набора данных.
- Прогноз: для каждого примера берём предиктивные вероятности по всем классам и выбираем топ-3 по убыванию вероятностей. Сформируем строку labels_str как объединение выбранных меток через «|».
- Оценка: для каждого примера проверяем, есть ли пересечения между истинными метками и предсказанными топ-3; считаем попаданием если есть пересечение (с учётом количества истинных меток, как указано в задаче).
Пошаговый план реализации (с подробностями)
1) Подготовка данных
- Входные поля: app_name, full_description, shortDescription, labels_str.
- Если full_description отсутствует, используйте только shortDescription и app_name в качестве текста.
- Если всех текстов нет, используйте app_name как текстовую информацию.
- Разделите данные на обучающую и тестовую выборки (например, 80/20 или кросс-валидация).
2) Преобразование текста
- Объединяйте текстовые поля: text = (app_name or '') + ' ' + (full_description or '') + ' ' + (shortDescription or '')
- Преобразование: приводим к нижнему регистру, удаляем лишние пробелы.
- Опционально: удаление неанглийских символов/перевод на английский, если данные неоднородны.
- Разделение на токены и построение TF-IDF матрицы: используйте TfidfVectorizer(stop_words='english', ngram_range=(1, 2)) чтобы уловить специфические фразы.
3) Подготовка меток
- Разделите labels_str по символу '|', получив список истинных меток для каждого примера.
- Соберите полный лейбл-словарь: уникальные метки во всем наборе данных.
- Преобразуйте истинные метки в бинарный вектор (MultiLabelBinarizer или схожий подход).
4) Обучение модели
- Разделение данных: X_train, X_val, y_train, y_val.
- Модель: OneVsRestClassifier на основе LogisticRegression. Это дает вероятности принадлежности к каждому классу.
- Параметры:
- LOGISTIC REGRESSION: solver='liblinear' или 'lbfgs', max_iter=1000, C=1.0
- OneVsRestClassifier поддерживает predict_proba (нужен базовый Estimator, который умеет вероятность).
- Обучение: обучаем на X_train, y_train.
5) Прогноз и постобработка
- Для каждого примера в тестовой выборке получаем predict_proba по всем классам.
- Сортируем по вероятности и берем топ-3 индексов. Преобразуем их обратно в метки (label_list[i]).
- Формируем строку вывода: top1|top2|top3 (если меньше трёх доступно — используем доступные).
- Если у примера реально нет предсказанных меток, можно оставить пустую строку, но обычно топ-3 найдётся.
6) Оценка H@3
- Для каждого примера сравниваем предсказанные топ-3 метки с истинными метками (из labels_str).
- Правило попадания:
- если у примера одна истинная метка и она в топ-3 → попадание
- если две или три истинные метки и любая из них в топ-3 → попадание
- H@3 = (число попаданий) / (общее число примеров)
- Важно: формула учитывает наличие хотя бы одной правильной метки в топ-3 (с учётом числа истинных меток).
7) Вывод в формате TSV
- Создайте файл с двумя столбцами: app_name labels_str
- Для каждого примера запишите app_name и строку-предложение меток через '|', соответствующую топ-3 по уверенности.
8) Возможные улучшения (после бейзлайна)
- Использовать более продвинутые модели: LinearSVC с калибровкой вероятностей (CalibratedClassifierCV) для получения качественных оценок вероятности.
- Добавить character-level TF-IDF (ngram_range=(3,5)) для улавливания опечаток и необычных сочетаний слов.
- Применить стемминг/lemmatization или обновлённый Preprocessing для русского текста, если цель — русский контент (в нашем случае категории на английском — фокус на английском тексте).
- Подыскать дополнительные признаки: наличие ключевых слов/паттернов в описаниях, эмбеддинги.
- Использовать трансформерные модели (например, ruBERT или мультиязычные BERT-модели) для лучшего понимания контекста. Это — более продвинутый бейзлайн, потребует больше вычислительных ресурсов.
Простой рабочий пример кода (бейзлайн)
Примечания:
- Требуется Python 3.x и следующие пакеты: pandas, scikit-learn, numpy.
- Предполагается, что данные загружаются из CSV/TSV, содержащего столбцы: app_name, full_description, shortDescription, labels_str.
Псевдокод/пример реализации:
- Импортируем библиотеки
import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import MultiLabelBinarizer
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.multiclass import OneVsRestClassifier
from sklearn.metrics import accuracy_score # для локальной оценки, не основная метрика
import numpy as np
- Загрузим данные
df = pd.read_csv('apps_dataset.csv') # или read_json, read_table, и т.д.
- Подготовим текстовую матрицу
def build_text(row):
parts = [row.get('app_name','')]
if isinstance(row.get('full_description'), str):
parts.append(row['full_description'])
if isinstance(row.get('shortDescription'), str):
parts.append(row['shortDescription'])
return ' '.join([p for p in parts if p])
df['text'] = df.apply(build_text, axis=1)
- Подготовим метки
df['labels'] = df['labels_str'].fillna('').apply(lambda s: [x for x in s.split('|') if x])
all_labels = sorted(set([lab for labs in df['labels'] for lab in labs]))
mlb = MultiLabelBinarizer(classes=all_labels)
y = mlb.fit_transform(df['labels'])
- Разделение данных
X_train, X_val, y_train, y_val = train_test_split(df['text'], y, test_size=0.2, random_state=42)
- Создаём пайплайн (TF-IDF + OneVsRest с логистической регрессией)
pipeline = Pipeline([
('tfidf', TfidfVectorizer(stop_words='english', ngram_range=(1, 2))),
('clf', OneVsRestClassifier(LogisticRegression(max_iter=1000, C=1.0)))
])
- Обучаем
pipeline.fit(X_train, y_train)
- Прогнозируем вероятности на валидации (для ручной оценки)
y_proba = pipeline.predict_proba(X_val)
- Получаем топ-3 предсказания
label_list = mlb.classes_
top3_pred = []
for probs in y_proba:
top_indices = probs.argsort()[-3:][::-1] # индексы топ-3
top_labels = [label_list[i] for i in top_indices]
top3_pred.append('|'.join(top_labels))
- Оценка H@3 (пример базовой проверки)
true_sets = [set(labels) for labels in df.loc[X_val.index, 'labels']]
pred_sets = [set(p.split('|')) for p in top3_pred]
hits = 0
for t, p in zip(true_sets, pred_sets):
if len(t) == 0:
continue
if len(t & p) > 0:
hits += 1
H_at_3 = hits / len(true_sets)
- Вывод в TSV
output = pd.DataFrame({
'app_name': df.loc[X_val.index, 'app_name'],
'labels_str': top3_pred
})
output.to_csv('predicted_labels.tsv', sep='\t', index=False)
Как работать с реальным тестовым набором
- Подготовьте тестовый набор, состоящий из тех же столбцов (app_name, full_description, shortDescription, labels_str) — но без labels_str, которые нужно предсказать.
- Прокрутите модель на обучающей части и получите предсказания для каждого приложения.
- Сохраните результат в TSV с двумя столбцами: app_name labels_str.
Как улучшать качество и результаты
- Добавляйте character-level признаки: ngram_range=(3,5) для TF-IDF, чтобы улавливать опечатки и нестандартные формы.
- Попробуйте CalibratedClassifierCV поверх LinearSVC или LogisticRegression, чтобы получить более надёжные вероятности.
- Используйте кросс-валидацию для подбора гиперпараметров (C для логистической регрессии, размер векторизатора и т.д.).
- Расширяйте признаки: добавляйте паттерны-слова ("bank", "game", "kid", "invoice" и т.д.), если известно распределение категорий.
- При дефиците данных для редких категорий можно применить частотную фильтрацию: не предлагать топ-3 категорий, если вероятность крайне низкая, но это риск снижения полноты.
Пример иллюстративного вывода
- Входной пример:
app_name: "QuickPay Pro"
full_description: "A secure mobile wallet for payments and transfers"
shortDescription: "Wallet and transfers in one app"
true labels: finance|tools
- Предполагаемый вывод:
app_name labels_str
QuickPay Pro finance|tools
Дополнительно: как рассчитать H@3 и интерпретировать баллы
- H@3 рассчитывается как доля примеров, для которых хотя бы одна истинная метка попала в ваш топ-3. Обычно это базовая метрика для задач мультиклассового и multilabel-классифицирования с ограниченным количеством предсказаний.
- В баллах учитывается percentileofscore(H@3s, H@3) — т.е. ваш рейтинг среди участников соревнования. Чтобы поднять баллы, улучшайте бейзлайн: используйте более продвинутые текстовые признаки, разные модели, кросс-валидацию и т.д.
Если хотите, могу привести точный рабочий ноутбук (ноутбук Jupyter) со всеми шагами, готовый под ваши данные: загрузка CSV, обучение, предсказания и экспорт TSV. Также могу помочь адаптировать код под конкретную структуру вашего набора данных (название столбцов, кодировку файлов и т.д.).