{"id":14277,"url":"\/distributions\/14277\/click?bit=1&hash=17ce698c744183890278e5e72fb5473eaa8dd0a28fac1d357bd91d8537b18c22","title":"\u041e\u0446\u0438\u0444\u0440\u043e\u0432\u0430\u0442\u044c \u043b\u0438\u0442\u0440\u044b \u0431\u0435\u043d\u0437\u0438\u043d\u0430 \u0438\u043b\u0438 \u0437\u043e\u043b\u043e\u0442\u044b\u0435 \u0443\u043a\u0440\u0430\u0448\u0435\u043d\u0438\u044f","buttonText":"\u041a\u0430\u043a?","imageUuid":"771ad34a-9f50-5b0b-bc84-204d36a20025"}

Как родить сына при помощи 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/photo_4@16-05-2022_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

Ответить
Развернуть ветку
Павел Рудой

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

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

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

Развернуть ветку
0 комментариев
Раскрывать всегда