Немного о нас
Я профессионально занимаюсь машинным обучением (МЛ) и активно участвовал в соревнованиях на Kaggle. Это позволяет мне развивать навыки и исследовать новые решения, но Kaggle занимает много времени и не приносит дохода. Из-за этого я и мои друзья решили создать что-то свое, что, возможно, превратится в стартап в будущем (как говорится, "каждое хобби должно заканчиваться стартапом"). Наша команда состоит из двух специалистов по машинному обучению и одного фронтенд-разработчика. Специалисты по МЛ работают над бэкендом, в то время как фронтенд-разработчик занимается своими прямыми обязанностями.
Исходя из этого, мы решили попробовать создать мобильные игры с использованием техник машинного обучения, даже если это будет лишь одним из элементов игры.
Наш первый проект
Воодушевленные подкастами и статьями, мы решили, что создавать "игру мечты" сразу не стоит. Вместо этого, предпочли начать с проекта попроще, чтобы изучить все тонкости процесса, учитывая наш график работы — полноценный фултайм был исключен.
На одном из подкастов я узнал, что эффективная стратегия для старта — это создание MVP (минимально жизнеспособного продукта) для Android, и если проект приживется, тогда расширяться на iOS, заложив основу для мультиплатформенности с самого начала.
В качестве первого проекта мы выбрали приложение, где пользователь должен угадать фильм по кадру. В процессе разработки мы решили добавить рейтинги и интересные факты о фильмах. Несмотря на возникновение множества идей, в определенный момент мы остановили разработку дополнительных функций, чтобы не усложнять первый проект.
Хотя мы с другом хорошо разбирались в машинном обучении и бэкенде, разработка фронтенда для мобильных устройств оказалась сложной. После нескольких неудачных попыток сделать это самостоятельно, мы наняли знакомого за символическую плату — он хотел получить опыт в мобильной разработке. Хотя он сделал большую часть работы, в итоге он покинул проект, но мы расстались по-дружески.
В итоге, к нашей команде присоединился еще один разработчик, мотивированный идеей, как и мы и нас стало трое. Он успешно завершил разработку фронтенда до первой публикации приложения.
Как мы искали нужные кадры из фильма
Процесс обработки начинался с разделения фильма на сцены, используя библиотеку OpenCV. Определение смены сцен осуществлялось путем анализа статистических изменений яркости кадров. Если среднее значение абсолютных различий в яркости кадров превышало заданный порог, это рассматривалось как начало новой сцены.
Предварительно мы рассматривали использование нейросетевой модели YOLO для выявления объектов в кадрах. Однако, несмотря на высокую точность YOLO в классификации и идентификации объектов, мы столкнулись с проблемой излишней фильтрации важных кадров. Например, в известной сцене из фильма "Форрест Гамп" с летящим пером, важные для сюжета кадры не содержали распознаваемых объектов, что привело к их исключению. В результате отказались от использования YOLO в пользу более традиционных методов выбора кадров, которые лучше справлялись с задачей сохранения визуально значимых моментов.
Кроме того, для удаления дублирующихся кадров использовалась техника хэширования изображений с помощью библиотеки ImageHash. Это позволило исключить повторы и сократить объем данных в датасете.
Ниже приведен фрагмент кода, демонстрирующий основные шаги обработки видео:
def scene_change_detection(movie_path, output_folder, threshold=30.0, frames_per_scene=3):
"""
Detects scene changes and extracts n random frames from each scene.
Uses OpenCV for frame processing and scene change detection.
:param movie_path: Path to the movie file.
:param output_folder: Folder to save the extracted frames.
:param threshold: Threshold for detecting scene changes based on frame differences.
:param frames_per_scene: Number of random frames to extract from each scene.
"""
if not os.path.exists(output_folder):
os.makedirs(output_folder)
cap = cv2.VideoCapture(movie_path)
if not cap.isOpened():
print("Error: Could not open video.")
return
ret, prev_frame = cap.read()
if not ret:
print("Error: Could not read the first frame.")
return
prev_frame_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
frame_buffer = []
scene_count = 0
while True:
ret, curr_frame = cap.read()
if not ret:
break
frame_buffer.append(curr_frame)
curr_frame_gray = cv2.cvtColor(curr_frame, cv2.COLOR_BGR2GRAY)
frame_diff = cv2.absdiff(curr_frame_gray, prev_frame_gray)
diff_score = np.mean(frame_diff)
if diff_score > threshold:
if len(frame_buffer) > frames_per_scene:
scene_count += 1
selected_frames = random.sample(frame_buffer, frames_per_scene)
for idx, frame in enumerate(selected_frames):
frame_name = f"scene_{scene_count}_frame_{idx}.jpg"
cv2.imwrite(os.path.join(output_folder, frame_name), frame)
frame_buffer.clear()
prev_frame_gray = curr_frame_gray
cap.release()
print(f"Scene detection complete. {scene_count} scenes detected.")
На подготовленном датасете мы обучили нейронную сеть, используя архитектуру EfficientNetV2 M, известную своей эффективностью в задачах классификации изображений. Сеть помогала нам отличать "хорошие" кадры от "плохих", опираясь на способность модели выявлять важные визуальные особенности, даже если прямо в кадре не было объектов, таких как люди или заметные предметы. Например, в сцене из фильма "Форрест Гамп" важным может быть кадр с парящим пером, хотя и не содержит персонажей.
Для дополнительной фильтрации и исключения чрезмерно похожих кадров в финальном приложении, из модели также извлекались эмбеддинги кадров. Эти эмбеддинги представляли собой векторы, которые характеризовали содержание кадра на глубоком уровне, позволяя нам точно сравнивать и отсеивать схожие изображения.
Этот подход не только улучшил качество выбора кадров для датасета, но и обеспечил более глубокую и осмысленную подготовку данных для нашего финального продукта.
Интересные факты
Процесс добавления интересных фактов о фильмах оказался эффективным благодаря использованию ChatGPT 3.5. Этот выбор был обусловлен более низкой стоимостью использования по сравнению с более новыми версиями, при этом сохраняя достаточный уровень точности и информативности ответов.
Мы подготовили специально сформулированные запросы (промпты), которые были направлены на извлечение данных и фактов, специфичных для каждого фильма. Эти запросы постепенно совершенствовались через несколько итераций, позволяя уточнить и улучшить качество и релевантность информации.
Ключевой аспект нашей работы заключался в оптимизации промптов, что требовало детального понимания возможностей и ограничений ChatGPT. Например, мы начали с базовых запросов типа "Расскажи интересный факт о фильме [название фильма]", и постепенно добавляли уточнения и контекст для улучшения результатов. Тестирование различных формулировок промптов позволило максимально эффективно использовать модель для генерации точной и полезной информации.
После настройки промптов процесс генерации фактов стал проходить гладко, и каждый фильм обрабатывался быстро и без значительных ошибок, обеспечивая надежную и ценную добавку к нашему контенту.
Дизайн приложения
Поскольку в нашей команде не было профессиональных дизайнеров, мы решили использовать нейронные сети для создания дизайна приложения. Мы выбрали MidJourney и DALL-E для этой задачи, что позволило нам автоматизировать процесс и получить качественные результаты без привлечения специалистов в области дизайна.
Применение MidJourney и DALL-E дало возможность экспериментировать с различными стилями и элементами дизайна, адаптируя их под уникальные требования нашего приложения. Ниже представлен пример таких генераций, демонстрирующих эффективность использованных подходов.
Работа с публикацией
Первый опыт публикации приложения в Google Play оказался для меня довольно сложным. Кажется, что я столкнулся со всеми возможными проблемами: от непонимания процесса до постоянного исправления ошибок. Несмотря на множество просмотренных видео и статей, простого и понятного руководства, объясняющего, как все сделать правильно с первого раза, я так и не нашел.
Постскриптум
Также интересен опыт сотрудничества: если у вас есть идеи или опыт в области разработки игр, предлагаю объединить усилия для создания чего-то действительно впечатляющего. Давайте вместе реализуем что-то уникальное!