{"id":13571,"url":"\/distributions\/13571\/click?bit=1&hash=d83cff4565300d1a2d0608fa73dd700b196f4b77356ac6255703ca3cdf2503d0","title":"\u041a\u043e\u043b\u043b\u0430\u0431\u044b, \u0440\u0435\u044e\u0437\u044b, \u043a\u043e\u043b\u043b\u0430\u0436\u0438... \u0414\u043b\u044f \u0447\u0435\u0433\u043e \u0432\u0441\u0451 \u044d\u0442\u043e?","buttonText":"\u0423\u0437\u043d\u0430\u0442\u044c","imageUuid":"bf0e0fe0-842c-5899-bb40-4efc00426ccf","isPaidAndBannersEnabled":false}
PABLO

Как родить сына при помощи Python?

В комментариях под другой статьей про машинное обучение поступило довольно заманчивое предложение:

«Почему бы и нет?» подумали мы и решили принять данный челлендж. Для достижения такого «серьёзного» результата нам понадобится Python и чат знакомств.

Используя Python, мы выгрузим заранее просмотренные анкеты, и при помощи каких-нибудь хитрых алгоритмов будем ранжировать новые профиля наших испытуемых на основе уже классифицированных. Так как нам нужны данные для дальнейшей автоматической классификации анкет, в начале попробуем просмотреть около 1000 профилей вручную. «Почему около 1000 профилей?» - спросите вы. На самом деле, результат этого опыта будет зависеть от точности. Чем больше профилей мы возьмем, тем точнее будет наш результат. И мы предполагаем, что примерно 1000 анкет нас устроит.

Этап 1. Просмотр анкет потенциальных вторых половинок.

Нам нужно просмотреть профили для того, чтобы сформировать подготовленный набор данных для дальнейшего использования в программе. Классификация весьма тривиальная, если фотография и описание анкеты понравились, значит ставим «лайк», иначе – «дизлайк».

Этап 2. Выгрузка и предобработка данных для дальнейшего использования.

Изначально наш чат знакомств находился в ВК, и мы неразумно полагали, что сможем получить от поддержки разрешение на использование блока messages из vkAPI. Но, как и следовало ожидать, ВК не захотели выдавать такие права слишком наглым молодым людям. Поэтому, немного погрустив, мы решили перебраться в Телеграмм и вновь перейти к 1 этапу, то есть опять найти чат знакомств и прокликать анкеты.

В Телеграмме для выгрузки данных не пришлось изобретать велосипед и что-то писать: там просто есть кнопка «Экспорт истории чата». Её можно найти, выбрав необходимую беседу чата и нажать по трем точкам в правом верхнем углу. Теперь мы без проблем выгрузим требующиеся данные: выберем в «Настройках экспорта» пункты «Фотографии» и «Стикеры», в формате изменим на json и нажмём «Экспортировать».

{ "id": 5015, "type": "message", "date": "2022-05-16T20:42:48", "from": "ChatBot", "from_id": "user1234567890", "photo": "photos/[email protected]_20-42-48.jpg", "width": 720, "height": 720, "text": "Ира, 22, Иркутск – Райан Гослинг в фильме Драйв" }, { "id": 5016, "type": "message", "date": "2022-05-16T20:42:53", "from": "Павел", "from_id": "user9876543210", "text": "❤️" }

Вот такие данные хранятся в json файле. Здесь есть место расположения фотографии человека, описание анкеты, а также наши ответы на них. Теперь достанем оттуда данные.

Подключим библиотеки и будем считывать данные из json файла:

import json import cv2 import face_recognition import numpy as np from tqdm import tqdm from scipy.spatial.distance import pdist # считываем данные из json файла with open('result.json', 'r', encoding='utf-8') as json_file: messages = json.load(json_file)["messages"]

Далее, находим среди всех сообщений бота: анкеты и наши ответы на них. Собираем это всё в один массив:

all_profiles = [] # заполняем массив анкет for message in messages: # проверка, является ли сообщение анкетой is_profile = message["from"] == "chatBot" and "photo" in message and message["text"] != '' # если это анкета, добавляем в массив if is_profile: # только если это не моя анкета) if "Павел" in message["text"]: continue data = { "photo": message["photo"], "description": message["text"], "label": 0 } all_profiles.append(data) # проверка, является ли сообщение оценкой анкеты is_estimation = message["from"] == "Павел" and (message["text"] == "👎" or message["text"] == "❤") # если это оценка, то проставляем её последней анкете if is_estimation: last_elem = len(all_profiles) - 1 if message["text"] == "👎": all_profiles[last_elem]["label"] = 0 else: all_profiles[last_elem]["label"] = 1

Отлично. Теперь у нас есть массив со всеми просмотренными анкетами и оценками на эти анкеты. Настало время определиться, как мы, собственно, будем их классифицировать. Изначально у нас были идеи пойти в сторону нейросетей и классифицировать через них. Но что-то не задалось, так как это оказалось слишком сложным и трудоёмким процессом. Поэтому мы решили реализовать «гениальный» алгоритм, который бы подбирал людей по схожести. То есть, наш алгоритм будет сравнивать новую фотографию с теми, которые мы уже отсмотрели и «лайкнули», чтобы выяснить, будет ли потенциальная вторая половинка по фотографии подходить нашему вкусу.

Для этого нам понадобятся библиотека face_recognition для распознавания лица и построения вектора значений и библиотека OpenCV для работы с изображениями:

face_recognition:

pip install dlib

pip install face_recognition

OpenCV:

pip3 install opencv-python

Итак, теперь обойдём все профили, которые мы предварительно записали в all_profiles, и для подходящих профилей построим вектор лица. Подходящие анкеты – это те анкеты, на которые мы поставили «лайк», и в которых указанный возраст больше или равен 18. Во многих чатах знакомств изначально ставятся фильтры по возрасту, тем не менее мы решили подстраховаться, так как у нас иногда встречались анкеты, где возраст был меньше 18.

# проходим по всем профилям из массива for profile in tqdm(all_profiles): age = 0 # получаем возраст из описания профиля, если не можем его получить, то считаем его за 0 if isinstance(profile["description"], str) and profile["description"].split()[1][:-1].isdigit(): age = int(profile["description"].split()[1][:-1]) # выбираем профили старше 18 и с оценкой "лайк" if profile["label"] == 1 and age >= 18: # считываем картинку профиля img = cv2.imread(profile["photo"]) # пытаемся получить вектор лица try: vector = face_recognition.face_encodings(img)[0] except IndexError: continue # добавляем полученный вектор в массив векторов vectors.append(vector)

Теперь с помощью numpy сохраняем набор векторов в отдельный файл:

vectors = np.array(vectors) np.save("vectors", vectors)

И можем переходить непосредственно к классификации.

Этап 3. Классификация анкет.

На основе сохранённых ранее векторов, мы будем проверять, подходит ли нам анкета или нет.

Считываем фото и строим для него вектор лица:

img = cv2.imread("test/8.jpg") # пытаемся построить вектор лица try: vector = face_recognition.face_encodings(img)[0] except IndexError: print("Лицо не распознано!") exit(0)

Подгружаем сохранённый набор векторов и обходим его циклом, находя для каждого вектора из набора евклидово расстояние между ним и новым вектором с помощью pdist, который мы ранее импортировали:

for elem in vectors: answer = pdist([vector, elem], "euclidean") result.append(answer[0])

И наконец, выводим ответ:

print(f"Average from numpy: {np.average(result)}") print(f"Median from numpy: {np.median(result)}") # выводим финальный вердикт if np.average(result) > 0.72: print("dislike") else: print("like")
Пример 1. Среднее значение евклидовых расстояний

Здесь для получения ответа мы используем numpy.average – среднее значение евклидовых расстояний между векторами: если оно больше 0.72, то профиль нам не подходит. Вообще, разработчик face_recognition заявлял, что, если евклидово расстояние между векторами меньше 0.6, то скорее всего на 2 фотографиях один и тот же человек. Соответственно значение до 0.72, которое мы получили исключительно экспериментальным путём, показывает, что 2 человека весьма похожи.

Не баг, а фича.

А теперь о хорошем! Мало того, что наша программа с успехом распознаёт и классифицирует лица представительниц прекрасного пола из реального мира, так ещё она справляется с распознаванием лиц мультипликационных дам.

Анкета 1. «Микаса, 19, Парадиз – в свободное время уничтожаю титанов, жду того, кто подарит мне красный шарф»

Например конкретно эта леди весьма успешно проходит классификацию и с показателем numpy.average равным 0.69 легко получает «лайк».

Заключение

Сегодня нами был сделан первый большой шаг в сторону рождения сына: дерево мы уже «вырастили», девушек для потенциального знакомства выбрали, осталось только начать заливать фундамент под будущий дом.

В заключении можно сказать, что, естественно, ни на какую серьёзность мы не претендуем, всё это было сделано исключительно в развлекательных целях. И хотя мы не сделали полную автоматизацию, в целом свою задачу выполнили. В будущем можно дописать программу так, чтобы она без участия человека «прокликивала» анкеты, и при взаимном «лайке» пересылала профиль в личные сообщения или сохраняла его где-то на диске. Вероятно, при добавлении новых функций и идей, мы найдем применение наших функций в реальных задачах.

Весь код можно найти на гитхабе.

0
3 комментария
Jinny

Главное питон не должен быть вялым.

Ответить
Развернуть ветку
Ника Авдонина

шутка просто 10000000 из 10

Ответить
Развернуть ветку
Willia

ты:просто написал ей привет
она: родила вам сына с помощью Python

Ответить
Развернуть ветку

Комментарий удален модератором

Развернуть ветку
Читать все 3 комментария
null