Вопрос или проблема
Я пытаюсь предварительно вычислить коэффициенты сходства для набора данных из 350 тысяч записей, но вычисления очень медленные из-за количества категорий, расчета временного сходства и обработки строкового сходства. Я уже реализовал несколько оптимизаций, включая использование GPU-ускорения, установление нижнего порога, вычисление только верхней половины матрицы сходства и использование типов данных float32. Несмотря на эти усилия, процесс все еще предполагается завершить только через несколько дней на одной машине A100. Есть ли какие-либо предложения, как еще больше улучшить производительность, чтобы завершить его за часы, а не за дни?
часть кода:
def calculate_time_similarity(date1: str, date2: str, time_decay_factor: float = 0.1) -> float:
"""
Рассчитывает сходство между двумя датами с экспоненциальным спадом.
Args:
date1: Первая дата выпуска (формат: YYYY/MM/DD)
date2: Вторая дата выпуска (формат: YYYY/MM/DD)
time_decay_factor: Контролирует, насколько быстро сходство уменьшается с разницей во времени.
Returns:
Оценка сходства от 0 до 1
"""
try:
d1 = datetime.strptime(date1, '%Y/%m/%d')
d2 = datetime.strptime(date2, '%Y/%m/%d')
# Рассчитать разницу во времени в годах
time_diff = abs((d1 - d2).days) / 365.0
# Формула экспоненциального спада: exp(-λt)
similarity = np.exp(-time_decay_factor * time_diff)
return similarity
except (ValueError, TypeError):
# Возвращает сходство 0, если даты недействительны
return 0.0
def prepare_text_features(df: pd.DataFrame) -> np.ndarray:
"""
Обработка текстовых столбцов с использованием векторизации TF-IDF.
Args:
df: DataFrame, содержащий текстовые столбцы
Returns:
Совмещенная матрица текстовых признаков
"""
text_columns = ['description', 'title', 'series']
text_features = {}
for col in text_columns:
# Инициализация TF-IDF векторизатора с японским токенайзером
tfidf = TfidfVectorizer(
tokenizer=tokenize_japanese,
max_features=1000, # Ограничение функций, чтобы предотвратить взрыв размерности
min_df=2, # Минимальная частота документов
max_df=0.95 # Максимальная частота документов
)
# Замена NA значений на пустую строку
text_series = df[col].fillna('')
# Применение метода обучения и преобразования к текстовым данным
text_features[col] = tfidf.fit_transform(text_series)
# Совмещение текстовых функций с помощью горизонтального объединения
combined_text_features = np.hstack([
matrix.toarray() for matrix in text_features.values()
])
return combined_text_features
def prepare_categorical_features(df: pd.DataFrame) -> np.ndarray:
"""
Подготовка категориальных признаков для рекомендации на основе контента.
Args:
df: DataFrame, содержащий категориальные признаки
Returns:
Совмещенная матрица категориальных признаков
"""
feature_matrices = {}
# Обработка столбцов на основе списков
list_columns = ['hashtags', 'genre', 'performer']
for col in list_columns:
mlb = MultiLabelBinarizer(sparse_output=False) # Изменено на False для плотного вывода
processed_col = df[col].apply(lambda x: [] if pd.isna(x) or x == [] else
[str(item).strip() for item in eval(x) if str(item).strip()])
feature_matrices[col] = mlb.fit_transform(processed_col)
# Обработка столбцов с одним значением
single_columns = ['director', 'label', 'maker', 'series']
for col in single_columns:
dummies = pd.get_dummies(df[col], prefix=col, dummy_na=True)
feature_matrices[col] = dummies.values
# Объединение всех категориальных признаков
combined_features = np.hstack([matrix for matrix in feature_matrices.values()])
return combined_features
Ответ или решение
Оптимизация вычисления схожести для набора данных из 350 тысяч записей — задача не из легких, особенно с учетом большого количества категорий, временных вычислений и обработки строк для вычисления их схожести. Рассмотрим подходы, которые могут помочь ускорить этот процесс.
Теория
Вычисление схожести обычно включает в себя обработку различных типов данных и использование нескольких алгоритмов для достижения точных результатов. В вашем случае, вы уже применили несколько оптимизаций, таких как использование GPU A100, снижение порога, расчет только верхней половины матрицы схожести и использование ниже точности при помощи float32. Однако, из-за размера и гетерогенности данных процесс все еще занимает значительное время.
Основные направления для дальнейшей оптимизации включают:
- Улучшенная обработка данных: Избегание излишней обработки данных и более эффективные алгоритмы для обработки строк и категорий.
- Улучшенная параллелизация: Использование всех доступных вычислительных ресурсов, включая многопоточность и распределенные вычисления.
- Алгоритмическая оптимизация: Применение более эффективных алгоритмов или их адаптация под вашу задачу.
- Оптимизация памяти: Уменьшение объемов данных, подгружаемых в оперативную память.
Пример
-
Улучшенная обработка данных:
- Использование FAISS или Annoy: Для высокоразмерных данных библиотеки, такие как FAISS от Facebook или Annoy от Spotify, могут значительно снизить время вычислений через структурированные индексы для поиска ближайших соседей.
-
Параллелизация:
- Используйте Dask или Apache Spark: Эти инструменты помогут распределить вычисления по кластеру машин. Даже на одной машине Dask может существенно улучшить использование ресурсов.
- Многопоточность: Используйте Python-библиотеки, такие как
concurrent.futures
с ThreadPoolExecutor или ProcessPoolExecutor для распределения задач между потоками или процессами.
-
Алгоритмическая оптимизация:
- Снижение сложности алгоритмов: Если возможно, пересмотрите алгоритмы на наличие излишних операций. Возможно, стоит использовать более простые алгоритмы подсчета схожести для начальной фильтрации, оставляя более сложные для уже отфильтрованных данных.
-
Оптимизация памяти:
- Использование более эффективных структур данных: Например, рассмотрите возможность использования библиотеки PyArrow для более эффективного управления памятью и пайплайна данных.
- Управление объемом данных в оперативной памяти: Запись промежуточных результатов на диск для освобождения оперативной памяти, что тоже может снизить время вычислений.
Применение
Теперь рассмотрим, как это может быть применено в вашем коде:
-
FAISS для поиска ближайших соседей: Интегрируйте FAISS для создания индексов и поиска схожих записей. Это может значительно сократить время на выполнение операций поиска по схожести.
-
Dask для обработки данных: Разбейте ваш процесс на задачи, которые можно выполнять параллельно, распределив их между несколькими потоками или даже узлами. Dask позволяет работать с данными объемов больше, чем может поместиться в памяти одной машины при помощи ограничения блоков данных.
-
Переписывание временной функции: Возможно, стоит пересмотреть функцию расчета времени, например, приближая значение эквивалентным, но менее затратным способами — такие приближения должны быть проверены на точность.
-
Оптимизация загрузки данных: Работайте с подмножествами данных — если вы можете разбить данные на кластерированные группы, вероятность погрешности снизится при увеличении количества данных для одновременной обработки.
Также не забывайте о профилировании. Используйте инструменты, такие как Py-Spy или cProfile, чтобы определить узкие места, которые могут быть скрыты. Таким способом можно найти дополнительные возможности для оптимизации.
Следует помнить, что каждая из предложенных методик требует тщательной настройки их под ваши конкретные данные и задачи, а также мониторинга производительности для достижения желаемых результатов.