Ниже — учебный бейзлайн-ноутбук ( beginner-friendly ), как решить задачу автоматической привязки приложений RuStore к до трёх категорий по тексту описания. Включаю пошаговую схему, простую реализацию на Python (scikit-learn) и объяснение метрики Hit@3. Вы можете запустить на своей партсерии данных и потом допиливать модель.
Кратко о задаче
- Данные на вход: app_name, full_description (если есть), shortDescription (если есть), labels_str (истинные категории через "|", например finance|tools).
- Цель: для каждого приложения предсказать до трёх категорий, упорядоченных по уверенности модели.
- Метрика оценки: Hit@3. Для каждого приложения считаем, попала ли хотя бы одна истинная метка в топ-3 предсказаний. Hitrate@3 = (число попаданий) / (число объектов). Итоговый показатель усредняется по всем тестовым примерам.
- Пояснение подхода (почему так)
- Текстовые данные удобно обрабатывать через TF-IDF векторизацию.
- Задача — многоклассная многометочная классификация (multi-label). Простой и эффективный бейзлайн: OneVsRestClassifier с логистической регрессией.
- Прогнозируем вероятности для каждой категории, сортируем по убыванию и берём топ-3.
- Важно корректно обрабатывать отсутствие full_description/shortDescription и объединять текст из нескольких полей.
- Что будем считать входом и как готовим данные
- Объединяем текстовый вход: text_i = app_name_i + " " + (full_description_i or "") + " " + (shortDescription_i or "")
- Метки: y_true_i — множество категорий из labels_str_i, разбитых по '|'.
- Стратегия обучения: разобьём данные на train/test; используется TF-IDF + OneVsRestClassifier на основе LogisticRegression.
- Шаги решения (пошагово)
- Шаг 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 по всему набору.
- Простой рабочий код (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 обычно работает.
- Как посчитать и интерпретировать 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.
- Как оформить итоговый 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 считаем отдельно по всему набору.
- Возможные улучшения бейзлайна
- Добавить character-level TF-IDF для захвата редких слов и сленга приложений.
- Использовать другой базовый классификатор: LinearSVC с вероятностями через калибровку (CalibratedClassifierCV).
- Поиграть с параметрами векторизации: max_features, ngram_range, использование stop_words (ru/none), analyzer='word' vs 'char'.
- Применить кластеризацию/управление классами (thresholding) для улучшения точности топ-3.
- Что вы получите и как использовать
- Вы получите модель, которая на вход получает app_name + тексты (полное описание и краткое), возвращает топ-3 категории по уверенности.
- Вы сможете экспортировать файл TSV для модераторов или для стейкхолдеров, где будет видно, какие три категории модель считает наиболее вероятными.
- Метрику Hit@3 можно использовать для сравнения разных подходов и гиперпараметров.
Если хотите, могу прислать готовый ноутбук (Jupyter) со встроенным кодом на Python, включая загрузку данных в формате CSV, полноценной парсинг-цифру и выдачу TSV с топ-3 предсказания и расчетом H@3. Также могу адаптировать под ваши данные: пример структуры CSV, названия колонок, разделитель и т.п. Сообщите, пожалуйста, какие у вас реальные колонки и как называются поля, и какой у вас набор тестовых данных.