Вам предстоит работать над каталогом мобильных приложений 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 – значение метрики данного участника.
Ниже — учебный бейзлайн-ноутбук ( beginner-friendly ), как решить задачу автоматической привязки приложений RuStore к до трёх категорий по тексту описания. Включаю пошаговую схему, простую реализацию на Python (scikit-learn) и объяснение метрики Hit@3. Вы можете запустить на своей партсерии данных и потом допиливать модель.
Кратко о задаче
- Данные на вход: app_name, full_description (если есть), shortDescription (если есть), labels_str (истинные категории через "|", например finance|tools).
- Цель: для каждого приложения предсказать до трёх категорий, упорядоченных по уверенности модели.
- Метрика оценки: Hit@3. Для каждого приложения считаем, попала ли хотя бы одна истинная метка в топ-3 предсказаний. Hitrate@3 = (число попаданий) / (число объектов). Итоговый показатель усредняется по всем тестовым примерам.
1) Пояснение подхода (почему так)
- Текстовые данные удобно обрабатывать через TF-IDF векторизацию.
- Задача — многоклассная многометочная классификация (multi-label). Простой и эффективный бейзлайн: OneVsRestClassifier с логистической регрессией.
- Прогнозируем вероятности для каждой категории, сортируем по убыванию и берём топ-3.
- Важно корректно обрабатывать отсутствие full_description/shortDescription и объединять текст из нескольких полей.
2) Что будем считать входом и как готовим данные
- Объединяем текстовый вход: text_i = app_name_i + " " + (full_description_i or "") + " " + (shortDescription_i or "")
- Метки: y_true_i — множество категорий из labels_str_i, разбитых по '|'.
- Стратегия обучения: разобьём данные на train/test; используется TF-IDF + OneVsRestClassifier на основе LogisticRegression.
3) Шаги решения (пошагово)
- Шаг 1. Подгрузка данных и предобработка
- Привести пустые описания к пустой строке.
- Объединить текстовые поля в один входной текст.
- Разбить labels_str по '|' и сохранить как множество истинных меток.
- Шаг 2. Векторизация и модель
- Векторизация: TfidfVectorizer(ngram_range=(1,2), max_features ~ 50000, language='ru' или без указания)
- Модель: OneVsRestClassifier(LogisticRegression(max_iter=1000, C=1.0, solver='liblinear'))
- Шаг 3. Обучение и оценка
- Разделение на train/test (например 80/20).
- Обучение модели на X_train, y_train.
- Получение матрицы вероятностей y_prob_test = clf.predict_proba(X_test) (размер: n_test x n_classes).
- Для каждого примера взяли топ-3 индексов классов по y_prob_test[i], упорядоченные по убыванию вероятности.
- Шаг 4. Формирование результатов и метрика Hit@3
- predicted_top3_i — список названий категорий (до 3) для i-го примера.
- hit_i = 1, если хотя бы одна истинная категория из y_true_test_i попала в predicted_top3_i, иначе 0.
- H@3 = mean([hit_i for все i]).
- Шаг 5. Вывод TSV
- Для каждого примера: app_name, labels_str (истинные), predicted_top3_str (верхние 3 предсказанные, через '|').
- Помимо этого можно сохранить отдельный столбец Hit3_i (0/1) для внутренней проверки, и отдельно вывести общий H@3 по всему набору.
4) Простой рабочий код (Python, минимальные зависимости)
Привожу минимальный каркас, который можно заполнить реальными данными. Этот код — базовый бейзлайн.
- Требования: Python 3.x, pandas, scikit-learn.
- Установка: pip install pandas scikit-learn
Пример кода (ключевые части):
- Импорт
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
import numpy as np
import pandas as pd
- Подготовка данных
# data – DataFrame с колонками: app_name, full_description, shortDescription, labels_str
data['text'] = data['app_name'].fillna('') + ' ' +
data['full_description'].fillna('') + ' ' +
data['shortDescription'].fillna('')
data['labels'] = data['labels_str'].fillna('').apply(lambda s: [t for t in s.split('|') if t])
# Соберём уникальные классы
all_labels = sorted({lab for labs in data['labels'] for lab in labs})
# Разделение на train/test
X = data['text'].values
y_true = data['labels'].values # список списков
X_tr, X_te, y_tr_lists, y_te_lists = train_test_split(X, y_true, test_size=0.2, random_state=42)
# Преобразование y в бинарную матрицу
mlb = MultiLabelBinarizer(classes=all_labels)
Y_tr = mlb.fit_transform(y_tr_lists)
Y_te = mlb.transform(y_te_lists)
# Модель: TF-IDF + OneVsRest с логистической регрессией
model = Pipeline([
('tfidf', TfidfVectorizer(stop_words='russian', ngram_range=(1, 2), max_features=60000)),
('clf', OneVsRestClassifier(LogisticRegression(max_iter=1000, C=1.0, solver='liblinear')))
])
# Обучение
model.fit(X_tr, Y_tr)
# Оценка и топ-3 предсказания
y_te_prob = model.predict_proba(X_te) # массив [n_te, n_classes]
# В некоторых версиях scikit-learn predict_proba возвращает список; приведём к массиву
if isinstance(y_te_prob, list):
y_te_prob = np.array(y_te_prob)
# Топ-3 индексов для каждого примера
top3_indices = np.argsort(-y_te_prob, axis=1)[:, :3] # верхние 3 по вероятности
# Карка для результатов
pred_top3_labels = []
hit_marks = []
for i, idxs in enumerate(top3_indices):
pred_labels = [mlb.classes_[j] for j in idxs]
pred_top3_labels.append('|'.join(pred_labels))
# Проверка попадания в топ-3 относительно истинных меток
true_set = set(y_te_lists[i])
hit = int(len(true_set.intersection(set(pred_labels))) > 0)
hit_marks.append(hit)
# Вычисляем Hit@3 по тесту
H_at_3 = np.mean(hit_marks)
# Формируем TSV-выгрузку: app_name, labels_str, pred_top3
output = pd.DataFrame({
'app_name': data.iloc[y_te_indexes]['app_name'], # индексы тестовой выборки
'labels_str': ['|'.join(y_te_lists[i]) for i in range(len(y_te_lists))],
'pred_top3': pred_top3_labels
})
# Сохранение
# output.to_csv('ru_store_top3.tsv', sep='\t', index=False)
# Вывод
print(f'Hitrate@3: {H_at_3:.4f}')
- Примечания по коду
- Я приводлю векторизатор с 1- и 2-граммами; можно увеличить max_features, поэкспериментировать с другими моделями (LinearSVC с вероятностями не всегда работает, но можно заменить на пакет Scikit-Learn LinearSVC с аппроксимацией).
- Если predict_proba недоступна для OneVsRestClassifier, можно использовать decision_function и преобразовать в вероятности через сигналы, но базовый путь с probas обычно работает.
5) Как посчитать и интерпретировать Hit@3
- Для каждого примера i мы имеем истинный набор меток y_true_i и топ-3 предсказанные метки top3_i.
- hit_i = 1, если y_true_i ∩ top3_i ≠ ∅, иначе 0.
- H@3 по всему набору = (Σ_i hit_i) / N, где N — число примеров в тестовом наборе.
- В итоговом файле можно сохранить:
- app_name
- labels_str (истинные)
- pred_top3 (тройка предсказанных категорий через '|')
- hit_i (0/1) — для анализа ошибок
- И отдельно вывести общий H@3.
6) Как оформить итоговый TSV (вариант)
- Рекомендую сделать две таблицы:
- Таблица с предсказаниями для оценки: app_name, labels_str, pred_top3, hit_i
- Файл обобщённой метрики: остается одна строка: Hitrate@3 = X.XXXX
- Если нужно строго 3 колонки (как в задании): можно сделать столбцы app_name | labels_str | Hitrate@3_per_row (0/1) — где Hitrate@3_per_row — это 1, если true label присутствует в pred_top3, иначе 0. А затем общий H@3 считаем отдельно по всему набору.
7) Возможные улучшения бейзлайна
- Добавить character-level TF-IDF для захвата редких слов и сленга приложений.
- Использовать другой базовый классификатор: LinearSVC с вероятностями через калибровку (CalibratedClassifierCV).
- Поиграть с параметрами векторизации: max_features, ngram_range, использование stop_words (ru/none), analyzer='word' vs 'char'.
- Применить кластеризацию/управление классами (thresholding) для улучшения точности топ-3.
8) Что вы получите и как использовать
- Вы получите модель, которая на вход получает app_name + тексты (полное описание и краткое), возвращает топ-3 категории по уверенности.
- Вы сможете экспортировать файл TSV для модераторов или для стейкхолдеров, где будет видно, какие три категории модель считает наиболее вероятными.
- Метрику Hit@3 можно использовать для сравнения разных подходов и гиперпараметров.
Если хотите, могу прислать готовый ноутбук (Jupyter) со встроенным кодом на Python, включая загрузку данных в формате CSV, полноценной парсинг-цифру и выдачу TSV с топ-3 предсказания и расчетом H@3. Также могу адаптировать под ваши данные: пример структуры CSV, названия колонок, разделитель и т.п. Сообщите, пожалуйста, какие у вас реальные колонки и как называются поля, и какой у вас набор тестовых данных.