Ниже — подробное решение и план действий для задачи автоматизации категоризации приложений 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; считаем попаданием если есть пересечение (с учётом количества истинных меток, как указано в задаче).
Пошаговый план реализации (с подробностями)
- Подготовка данных
- Входные поля: app_name, full_description, shortDescription, labels_str.
- Если full_description отсутствует, используйте только shortDescription и app_name в качестве текста.
- Если всех текстов нет, используйте app_name как текстовую информацию.
- Разделите данные на обучающую и тестовую выборки (например, 80/20 или кросс-валидация).
- Преобразование текста
- Объединяйте текстовые поля: text = (app_name or '') + ' ' + (full_description or '') + ' ' + (shortDescription or '')
- Преобразование: приводим к нижнему регистру, удаляем лишние пробелы.
- Опционально: удаление неанглийских символов/перевод на английский, если данные неоднородны.
- Разделение на токены и построение TF-IDF матрицы: используйте TfidfVectorizer(stop_words='english', ngram_range=(1, 2)) чтобы уловить специфические фразы.
- Подготовка меток
- Разделите labels_str по символу '|', получив список истинных меток для каждого примера.
- Соберите полный лейбл-словарь: уникальные метки во всем наборе данных.
- Преобразуйте истинные метки в бинарный вектор (MultiLabelBinarizer или схожий подход).
- Обучение модели
- Разделение данных: 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.
- Прогноз и постобработка
- Для каждого примера в тестовой выборке получаем predict_proba по всем классам.
- Сортируем по вероятности и берем топ-3 индексов. Преобразуем их обратно в метки (label_list[i]).
- Формируем строку вывода: top1|top2|top3 (если меньше трёх доступно — используем доступные).
- Если у примера реально нет предсказанных меток, можно оставить пустую строку, но обычно топ-3 найдётся.
- Оценка H@3
- Для каждого примера сравниваем предсказанные топ-3 метки с истинными метками (из labels_str).
- Правило попадания:
- если у примера одна истинная метка и она в топ-3 → попадание
- если две или три истинные метки и любая из них в топ-3 → попадание
- H@3 = (число попаданий) / (общее число примеров)
- Важно: формула учитывает наличие хотя бы одной правильной метки в топ-3 (с учётом числа истинных меток).
- Вывод в формате TSV
- Создайте файл с двумя столбцами: app_name labels_str
- Для каждого примера запишите app_name и строку-предложение меток через '|', соответствующую топ-3 по уверенности.
- Возможные улучшения (после бейзлайна)
- Использовать более продвинутые модели: 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. Также могу помочь адаптировать код под конкретную структуру вашего набора данных (название столбцов, кодировку файлов и т.д.).