Auto-Tinder был создан для обучения API с использованием Tensorflow и Python3, который изучает ваши интересы и автоматически играет в Tinder Swiping Game для вас.

В этом документе я собираюсь объяснить следующие шаги, которые были необходимы для создания автоматического затягивания:
Auto Tinder - это концептуальный проект, созданный чисто для веселья и образовательных целей. Он никогда не будет злоупотреблять, чтобы никому нанести вред или спамить платформу. Сценарии автоматического затягивания не должны использоваться с вашим профилем Tinder, поскольку они, безусловно, нарушают условия обслуживания Tinders.
Я написал эту часть программного обеспечения в основном из двух причин:
Первый шаг - выяснить, как приложение Tinder связывается с сервером Tinders Backend. Поскольку Tinder предлагает веб -версию своего портала, это так же просто, как и на Tinder.com, открывая Chrome Devtools и быстро рассмотрит сетевой протокол.
Контент, показанный на рисунке выше, был по запросу на https://api.gotinder.com/v2/recs/core, который выполняется при загрузке целевой страницы Tinder.com. Очевидно, что у Tinder есть какой-то внутренний API, который они используют для общения между передним и бэкэнд.
При анализе содержания /Recs /Core становится ясно, что эта конечная точка API возвращает список пользовательских профилей людей поблизости.
Данные включают (среди многих других полей), следующие данные:
{
"meta" : {
"status" : 200
},
"data" : {
"results" : [
{
"type" : " user " ,
"user" : {
"_id" : " 4adfwe547s8df64df " ,
"bio" : " 19y. " ,
"birth_date" : " 1997-17-06T18:21:44.654Z " ,
"name" : " Anna " ,
"photos" : [
{
"id" : " 879sdfert-lskdföj-8asdf879-987sdflkj " ,
"crop_info" : {
"user" : {
"width_pct" : 1 ,
"x_offset_pct" : 0 ,
"height_pct" : 0.8 ,
"y_offset_pct" : 0.08975463
},
"algo" : {
"width_pct" : 0.45674357 ,
"x_offset_pct" : 0.984341657 ,
"height_pct" : 0.234165403 ,
"y_offset_pct" : 0.78902343
},
"processed_by_bullseye" : true ,
"user_customized" : false
},
"url" : " https://images-ssl.gotinder.com/4adfwe547s8df64df/original_879sdfert-lskdföj-8asdf879-987sdflkj.jpeg " ,
"processedFiles" : [
{
"url" : " https://images-ssl.gotinder.com/4adfwe547s8df64df/640x800_879sdfert-lskdföj-8asdf879-987sdflkj.jpg " ,
"height" : 800 ,
"width" : 640
},
{
"url" : " https://images-ssl.gotinder.com/4adfwe547s8df64df/320x400_879sdfert-lskdföj-8asdf879-987sdflkj.jpg " ,
"height" : 400 ,
"width" : 320
},
{
"url" : " https://images-ssl.gotinder.com/4adfwe547s8df64df/172x216_879sdfert-lskdföj-8asdf879-987sdflkj.jpg " ,
"height" : 216 ,
"width" : 172
},
{
"url" : " https://images-ssl.gotinder.com/4adfwe547s8df64df/84x106_879sdfert-lskdföj-8asdf879-987sdflkj.jpg " ,
"height" : 106 ,
"width" : 84
}
],
"last_update_time" : " 2019-10-03T16:18:30.532Z " ,
"fileName" : " 879sdfert-lskdföj-8asdf879-987sdflkj.webp " ,
"extension" : " jpg,webp " ,
"webp_qf" : [
75
]
}
],
"gender" : 1 ,
"jobs" : [],
"schools" : [],
"show_gender_on_profile" : false
},
"facebook" : {
"common_connections" : [],
"connection_count" : 0 ,
"common_interests" : []
},
"spotify" : {
"spotify_connected" : false
},
"distance_mi" : 1 ,
"content_hash" : " slkadjfiuwejsdfuzkejhrsdbfskdzufiuerwer " ,
"s_number" : 9876540657341 ,
"teaser" : {
"string" : " "
},
"teasers" : [],
"snap" : {
"snaps" : []
}
}
]
}
}
Несколько вещей здесь очень интересны (обратите внимание, что я изменил все данные, чтобы не нарушать конфиденциальность этих лиц) :
Анализируя заголовки контента, мы быстро находим наши частные ключи API: X-Auth-Token .
Скочивая этот токен и переход к почтальству, мы можем подтвердить, что мы действительно можем свободно общаться с API Tinder с правильным URL и нашим токеном Auth.
Нажав немного через Tinders WebApp, я быстро обнаруживаю все соответствующие конечные точки API:
| Тип | URL | Описание |
|---|---|---|
| ПОЛУЧАТЬ | /v2/recs/core | Возвращает список людей поблизости |
| ПОЛУЧАТЬ | /v2/profile? include = account%2Cuser | Возвращает всю информацию о вашем собственном профиле |
| ПОЛУЧАТЬ | /v2/матчи | Возвращает список всех людей, которые совпадали с вами |
| ПОЛУЧАТЬ | /like/{user_id} | Любит человека с данным user_id |
| ПОЛУЧАТЬ | /pass/{user_id} | Передает человека с данным user_id |
Итак, давайте попадем в код. Мы будем использовать библиотеку запросов Python для общения с API и написать класс API обертки вокруг нее для удобства.
Точно так же мы пишем небольшой класс человека, который получает ответ API от Tinder, представляющего человека, и предлагает несколько основных интерфейсов для API Tinder.
Давайте начнем с класса человека. Он должен получать данные API, объект Tinder-API и сохранять все соответствующие данные в переменные экземпляра. Он также должен предложить некоторые основные функции, такие как «нравится» или «неприязнь», которые делают запрос на Tinder-API, что позволяет нам удобно использовать «some_person.like ()», чтобы понравиться профиль, который мы находим интересным.
import datetime
from geopy . geocoders import Nominatim
TINDER_URL = "https://api.gotinder.com"
geolocator = Nominatim ( user_agent = "auto-tinder" )
PROF_FILE = "./images/unclassified/profiles.txt"
class Person ( object ):
def __init__ ( self , data , api ):
self . _api = api
self . id = data [ "_id" ]
self . name = data . get ( "name" , "Unknown" )
self . bio = data . get ( "bio" , "" )
self . distance = data . get ( "distance_mi" , 0 ) / 1.60934
self . birth_date = datetime . datetime . strptime ( data [ "birth_date" ], '%Y-%m-%dT%H:%M:%S.%fZ' ) if data . get (
"birth_date" , False ) else None
self . gender = [ "Male" , "Female" , "Unknown" ][ data . get ( "gender" , 2 )]
self . images = list ( map ( lambda photo : photo [ "url" ], data . get ( "photos" , [])))
self . jobs = list (
map ( lambda job : { "title" : job . get ( "title" , {}). get ( "name" ), "company" : job . get ( "company" , {}). get ( "name" )}, data . get ( "jobs" , [])))
self . schools = list ( map ( lambda school : school [ "name" ], data . get ( "schools" , [])))
if data . get ( "pos" , False ):
self . location = geolocator . reverse ( f' { data [ "pos" ][ "lat" ] } , { data [ "pos" ][ "lon" ] } ' )
def __repr__ ( self ):
return f" { self . id } - { self . name } ( { self . birth_date . strftime ( '%d.%m.%Y' ) } )"
def like ( self ):
return self . _api . like ( self . id )
def dislike ( self ):
return self . _api . dislike ( self . id )Наша обертка API - это не больше, чем причудливый способ позвонить в Tinder API с помощью класса:
import requests
TINDER_URL = "https://api.gotinder.com"
class tinderAPI ():
def __init__ ( self , token ):
self . _token = token
def profile ( self ):
data = requests . get ( TINDER_URL + "/v2/profile?include=account%2Cuser" , headers = { "X-Auth-Token" : self . _token }). json ()
return Profile ( data [ "data" ], self )
def matches ( self , limit = 10 ):
data = requests . get ( TINDER_URL + f"/v2/matches?count= { limit } " , headers = { "X-Auth-Token" : self . _token }). json ()
return list ( map ( lambda match : Person ( match [ "person" ], self ), data [ "data" ][ "matches" ]))
def like ( self , user_id ):
data = requests . get ( TINDER_URL + f"/like/ { user_id } " , headers = { "X-Auth-Token" : self . _token }). json ()
return {
"is_match" : data [ "match" ],
"liked_remaining" : data [ "likes_remaining" ]
}
def dislike ( self , user_id ):
requests . get ( TINDER_URL + f"/pass/ { user_id } " , headers = { "X-Auth-Token" : self . _token }). json ()
return True
def nearby_persons ( self ):
data = requests . get ( TINDER_URL + "/v2/recs/core" , headers = { "X-Auth-Token" : self . _token }). json ()
return list ( map ( lambda user : Person ( user [ "user" ], self ), data [ "data" ][ "results" ]))Теперь мы можем использовать API, чтобы найти людей поблизости и взглянуть на их профиль, или даже как все они. Замените свой API-Token на X-Auth-Token, который вы нашли в консоли Chrome Dev ранее.
if __name__ == "__main__" :
token = "YOUR-API-TOKEN"
api = tinderAPI ( token )
while True :
persons = api . nearby_persons ()
for person in persons :
print ( person )
# person.like() Затем мы хотим автоматически загрузить некоторые изображения людей поблизости, которые мы можем использовать для обучения нашего ИИ. С «некоторыми» я имею в виду 1500-2500 изображений.
Во -первых, давайте расширим наш класс личности с помощью функции, которая позволяет нам загружать изображения.
# At the top of auto_tinder.py
PROF_FILE = "./images/unclassified/profiles.txt"
# inside the Person-class
def download_images ( self , folder = "." , sleep_max_for = 0 ):
with open ( PROF_FILE , "r" ) as f :
lines = f . readlines ()
if self . id in lines :
return
with open ( PROF_FILE , "a" ) as f :
f . write ( self . id + " r n " )
index = - 1
for image_url in self . images :
index += 1
req = requests . get ( image_url , stream = True )
if req . status_code == 200 :
with open ( f" { folder } / { self . id } _ { self . name } _ { index } .jpeg" , "wb" ) as f :
f . write ( req . content )
sleep ( random () * sleep_max_for )Обратите внимание, что я добавил несколько случайных сна здесь и там, просто потому, что мы, вероятно, будем заблокированы, если мы спам Tinder CDN и загрузите много картинок всего за несколько секунд.
Мы записываем все идентификаторы профиля народов в файл с именем «profiles.txt». Сначала сканируя документ, есть ли там конкретный человек, мы можем пропустить людей, с которыми мы уже столкнулись, и мы гарантируем, что мы не классифицируем людей несколько раз (вы увидите позже, почему это риск).
Теперь мы можем просто пройти поблизости поблизости и загружать их изображения в «неклассифицированную» папку.
if __name__ == "__main__" :
token = "YOUR-API-TOKEN"
api = tinderAPI ( token )
while True :
persons = api . nearby_persons ()
for person in persons :
person . download_images ( folder = "./images/unclassified" , sleep_max_for = random () * 3 )
sleep ( random () * 10 )
sleep ( random () * 10 )Теперь мы можем просто запустить этот сценарий и позволить ему работать в течение нескольких часов, чтобы получить несколько изображений профиля поблизости Hundret. Если вы пользователь Tinder Pro, время от времени обновляйте свое местоположение, чтобы получить новых людей.
Теперь, когда у нас есть куча изображений для работы, давайте создадим действительно простой и уродливый классификатор.
Он должен просто зацикливаться на всех изображениях в нашей «неклассифицированной» папке и откроет изображение в окне графического интерфейса. Щелкнув правой кнопкой мыши человека, мы можем пометить человека как «неприязнь», в то время как щелкнут налево, отмечает человека как «как». Это будет представлено в имени файла позже: 4tz3kjldfj3482.jpg будет переименован в 1_4tz3kjldfj3482.jpg , если мы помечаем изображение как «нравится» или 0_4tz3kjldfj3482.jpg . Метка, как/неприязнь, кодируется как 1/0 в начале фили.
Давайте использовать Tkinter, чтобы быстро написать этот графический интерфейс:
from os import listdir , rename
from os . path import isfile , join
import tkinter as tk
from PIL import ImageTk , Image
IMAGE_FOLDER = "./images/unclassified"
images = [ f for f in listdir ( IMAGE_FOLDER ) if isfile ( join ( IMAGE_FOLDER , f ))]
unclassified_images = filter ( lambda image : not ( image . startswith ( "0_" ) or image . startswith ( "1_" )), images )
current = None
def next_img ():
global current , unclassified_images
try :
current = next ( unclassified_images )
except StopIteration :
root . quit ()
print ( current )
pil_img = Image . open ( IMAGE_FOLDER + "/" + current )
width , height = pil_img . size
max_height = 1000
if height > max_height :
resize_factor = max_height / height
pil_img = pil_img . resize (( int ( width * resize_factor ), int ( height * resize_factor )), resample = Image . LANCZOS )
img_tk = ImageTk . PhotoImage ( pil_img )
img_label . img = img_tk
img_label . config ( image = img_label . img )
def positive ( arg ):
global current
rename ( IMAGE_FOLDER + "/" + current , IMAGE_FOLDER + "/1_" + current )
next_img ()
def negative ( arg ):
global current
rename ( IMAGE_FOLDER + "/" + current , IMAGE_FOLDER + "/0_" + current )
next_img ()
if __name__ == "__main__" :
root = tk . Tk ()
img_label = tk . Label ( root )
img_label . pack ()
img_label . bind ( "<Button-1>" , positive )
img_label . bind ( "<Button-3>" , negative )
btn = tk . Button ( root , text = 'Next image' , command = next_img )
next_img () # load first image
root . mainloop ()Мы загружаем все неклассифицированные изображения в список «unclassified_images», открываем окно Tkinter, упаковываем в него первое изображение, позвонив в Next_img () и измените размер изображения, чтобы вписаться на экран. Затем мы регистрируем два щелчка, кнопки левой и правой мыши и называем функции положительными/отрицательными, которые переименовывают изображения в соответствии с их меткой, и показываем следующее изображение.
Уродливая, но эффективная.
Для следующего шага нам нужно привести данные наших изображений в формат, который позволяет нам выполнять классификацию. Есть несколько трудностей, которые мы должны рассмотреть, учитывая наш набор данных.
Мы бороемся с этими проблемами:

Первая часть так же просто, как и использование подушки, чтобы открыть наше изображение и преобразовать его в Greyscale. Во второй части мы используем API обнаружения объектов TensorFlow с архитектурой сети Mobilenet, предварительно подготовленной в наборе данных COCO, который также содержит ярлык для «человека».
Наш сценарий для обнаружения человека имеет четыре части:
Вы находите файл .bp для таблицы Tensorflow Mobilenet Coco в моем репозитории GitHub. Давайте откроем его как график тензора:
import tensorflow as tf
def open_graph ():
detection_graph = tf . Graph ()
with detection_graph . as_default ():
od_graph_def = tf . GraphDef ()
with tf . gfile . GFile ( 'ssd_mobilenet_v1_coco_2017_11_17/frozen_inference_graph.pb' , 'rb' ) as fid :
serialized_graph = fid . read ()
od_graph_def . ParseFromString ( serialized_graph )
tf . import_graph_def ( od_graph_def , name = '' )
return detection_graphМы используем подушку для манипуляции с изображением. Поскольку TensorFlow нужны необработанные массивы Numpy для работы с данными, давайте напишем небольшую функцию, которая преобразует изображения наволочек в массивы Numpy:
import numpy as np
def load_image_into_numpy_array ( image ):
( im_width , im_height ) = image . size
return np . array ( image . getdata ()). reshape (
( im_height , im_width , 3 )). astype ( np . uint8 )Следующая функция принимает изображение и график Tensorflow, запускает сеанс TensorFlow, используя его и возвращает все информацию о обнаруженных классах (типах объектов), ограничивающих ящиках и оценках (уверенность в том, что объект был обнаружен правильно).
import numpy as np
from object_detection . utils import ops as utils_ops
import tensorflow as tf
def run_inference_for_single_image ( image , sess ):
ops = tf . get_default_graph (). get_operations ()
all_tensor_names = { output . name for op in ops for output in op . outputs }
tensor_dict = {}
for key in [
'num_detections' , 'detection_boxes' , 'detection_scores' ,
'detection_classes' , 'detection_masks'
]:
tensor_name = key + ':0'
if tensor_name in all_tensor_names :
tensor_dict [ key ] = tf . get_default_graph (). get_tensor_by_name (
tensor_name )
if 'detection_masks' in tensor_dict :
# The following processing is only for single image
detection_boxes = tf . squeeze ( tensor_dict [ 'detection_boxes' ], [ 0 ])
detection_masks = tf . squeeze ( tensor_dict [ 'detection_masks' ], [ 0 ])
# Reframe is required to translate mask from box coordinates to image coordinates and fit the image size.
real_num_detection = tf . cast ( tensor_dict [ 'num_detections' ][ 0 ], tf . int32 )
detection_boxes = tf . slice ( detection_boxes , [ 0 , 0 ], [ real_num_detection , - 1 ])
detection_masks = tf . slice ( detection_masks , [ 0 , 0 , 0 ], [ real_num_detection , - 1 , - 1 ])
detection_masks_reframed = utils_ops . reframe_box_masks_to_image_masks (
detection_masks , detection_boxes , image . shape [ 1 ], image . shape [ 2 ])
detection_masks_reframed = tf . cast (
tf . greater ( detection_masks_reframed , 0.5 ), tf . uint8 )
# Follow the convention by adding back the batch dimension
tensor_dict [ 'detection_masks' ] = tf . expand_dims (
detection_masks_reframed , 0 )
image_tensor = tf . get_default_graph (). get_tensor_by_name ( 'image_tensor:0' )
# Run inference
output_dict = sess . run ( tensor_dict ,
feed_dict = { image_tensor : image })
# all outputs are float32 numpy arrays, so convert types as appropriate
output_dict [ 'num_detections' ] = int ( output_dict [ 'num_detections' ][ 0 ])
output_dict [ 'detection_classes' ] = output_dict [
'detection_classes' ][ 0 ]. astype ( np . int64 )
output_dict [ 'detection_boxes' ] = output_dict [ 'detection_boxes' ][ 0 ]
output_dict [ 'detection_scores' ] = output_dict [ 'detection_scores' ][ 0 ]
if 'detection_masks' in output_dict :
output_dict [ 'detection_masks' ] = output_dict [ 'detection_masks' ][ 0 ]
return output_dictПоследний шаг - написать функцию, которая берет путь к изображению, открывает ее, используя подушку, вызывает интерфейс API обнаружения объекта и разбудит изображение в соответствии с ограничивающим ящиком обнаруженных лиц.
import numpy as np
from PIL import Image
PERSON_CLASS = 1
SCORE_THRESHOLD = 0.5
def get_person ( image_path , sess ):
img = Image . open ( image_path )
image_np = load_image_into_numpy_array ( img )
image_np_expanded = np . expand_dims ( image_np , axis = 0 )
output_dict = run_inference_for_single_image ( image_np_expanded , sess )
persons_coordinates = []
for i in range ( len ( output_dict [ "detection_boxes" ])):
score = output_dict [ "detection_scores" ][ i ]
classtype = output_dict [ "detection_classes" ][ i ]
if score > SCORE_THRESHOLD and classtype == PERSON_CLASS :
persons_coordinates . append ( output_dict [ "detection_boxes" ][ i ])
w , h = img . size
for person_coordinate in persons_coordinates :
cropped_img = img . crop ((
int ( w * person_coordinate [ 1 ]),
int ( h * person_coordinate [ 0 ]),
int ( w * person_coordinate [ 3 ]),
int ( h * person_coordinate [ 2 ]),
))
return cropped_img
return NoneВ качестве последнего шага мы пишем сценарий, который зацикливается на всех изображениях в папке «Unclassicified», проверяет, есть ли у них кодированная метка в имени копирует изображение в «классифицированной» папке с применением ранее разработанных шагов предварительной обработки:
import os
import person_detector
import tensorflow as tf
IMAGE_FOLDER = "./images/unclassified"
POS_FOLDER = "./images/classified/positive"
NEG_FOLDER = "./images/classified/negative"
if __name__ == "__main__" :
detection_graph = person_detector . open_graph ()
images = [ f for f in os . listdir ( IMAGE_FOLDER ) if os . path . isfile ( os . path . join ( IMAGE_FOLDER , f ))]
positive_images = filter ( lambda image : ( image . startswith ( "1_" )), images )
negative_images = filter ( lambda image : ( image . startswith ( "0_" )), images )
with detection_graph . as_default ():
with tf . Session () as sess :
for pos in positive_images :
old_filename = IMAGE_FOLDER + "/" + pos
new_filename = POS_FOLDER + "/" + pos [: - 5 ] + ".jpg"
if not os . path . isfile ( new_filename ):
img = person_detector . get_person ( old_filename , sess )
if not img :
continue
img = img . convert ( 'L' )
img . save ( new_filename , "jpeg" )
for neg in negative_images :
old_filename = IMAGE_FOLDER + "/" + neg
new_filename = NEG_FOLDER + "/" + neg [: - 5 ] + ".jpg"
if not os . path . isfile ( new_filename ):
img = person_detector . get_person ( old_filename , sess )
if not img :
continue
img = img . convert ( 'L' )
img . save ( new_filename , "jpeg" )Когда мы запускаем этот сценарий, все помеченные изображения обрабатываются и перемещаются в соответствующие подпапки в каталоге «классифицированного».
Для переподготовки мы просто будем использовать сценарий TensorFlows Retrain.py с моделью «Начало».
Позвоните в сценарий в корневом каталоге вашего проекта со следующими параметрами:
python retrain.py --bottleneck_dir=tf/training_data/bottlenecks --model_dir=tf/training_data/inception --summaries_dir=tf/training_data/summaries/basic --output_graph=tf/training_output/retrained_graph.pb --output_labels=tf/training_output/retrained_labels.txt --image_dir=./images/classified --how_many_training_steps=50000 --testing_percentage=20 --learning_rate=0.001Обучение занимает примерно 15 минут на GTX 1080 TI, с окончательной точностью около 80% для моего помеченного набора данных, но это сильно зависит от качества ваших входных данных и вашей маркировки.
Результатом обучающего процесса является переподготовка модели «Начало» в файле «TF/Training_Output/retrained_graph.pb». Теперь мы должны написать класс классификатора, который эффективно использует новые веса на графике TensorFlow, чтобы сделать прогноз классификации.
Давайте напишем класс классификатора, который открывает график в качестве сеанса и предлагает метод «классификации» с файлом изображения, который возвращает DICT с значениями уверенности, соответствующие нашими этикетками «положительные» и «отрицательные».
Класс принимает как вход как путь к графику, так и путь к файлу метки, как в нашей папке «tf/training_output/». Мы разрабатываем вспомогательные функции для преобразования файла изображения в тензор, который мы можем подавать в наш график, вспомогательную функцию для загрузки графика и метки и важную небольшую функцию, чтобы закрыть наш график после того, как мы закончим его.
import numpy as np
import tensorflow as tf
class Classifier ():
def __init__ ( self , graph , labels ):
self . _graph = self . load_graph ( graph )
self . _labels = self . load_labels ( labels )
self . _input_operation = self . _graph . get_operation_by_name ( "import/Placeholder" )
self . _output_operation = self . _graph . get_operation_by_name ( "import/final_result" )
self . _session = tf . Session ( graph = self . _graph )
def classify ( self , file_name ):
t = self . read_tensor_from_image_file ( file_name )
# Open up a new tensorflow session and run it on the input
results = self . _session . run ( self . _output_operation . outputs [ 0 ], { self . _input_operation . outputs [ 0 ]: t })
results = np . squeeze ( results )
# Sort the output predictions by prediction accuracy
top_k = results . argsort ()[ - 5 :][:: - 1 ]
result = {}
for i in top_k :
result [ self . _labels [ i ]] = results [ i ]
# Return sorted result tuples
return result
def close ( self ):
self . _session . close ()
@ staticmethod
def load_graph ( model_file ):
graph = tf . Graph ()
graph_def = tf . GraphDef ()
with open ( model_file , "rb" ) as f :
graph_def . ParseFromString ( f . read ())
with graph . as_default ():
tf . import_graph_def ( graph_def )
return graph
@ staticmethod
def load_labels ( label_file ):
label = []
proto_as_ascii_lines = tf . gfile . GFile ( label_file ). readlines ()
for l in proto_as_ascii_lines :
label . append ( l . rstrip ())
return label
@ staticmethod
def read_tensor_from_image_file ( file_name ,
input_height = 299 ,
input_width = 299 ,
input_mean = 0 ,
input_std = 255 ):
input_name = "file_reader"
file_reader = tf . read_file ( file_name , input_name )
image_reader = tf . image . decode_jpeg (
file_reader , channels = 3 , name = "jpeg_reader" )
float_caster = tf . cast ( image_reader , tf . float32 )
dims_expander = tf . expand_dims ( float_caster , 0 )
resized = tf . image . resize_bilinear ( dims_expander , [ input_height , input_width ])
normalized = tf . divide ( tf . subtract ( resized , [ input_mean ]), [ input_std ])
sess = tf . Session ()
result = sess . run ( normalized )
return result Теперь, когда у нас есть наш классификатор, давайте расширим класс «Человек» из более раннего и расширим его с помощью функции «прогноз_ликелизма», которая использует экземпляр классификатора, чтобы проверить, следует ли любить данный человек или нет.
# In the Person class
def predict_likeliness ( self , classifier , sess ):
ratings = []
for image in self . images :
req = requests . get ( image , stream = True )
tmp_filename = f"./images/tmp/run.jpg"
if req . status_code == 200 :
with open ( tmp_filename , "wb" ) as f :
f . write ( req . content )
img = person_detector . get_person ( tmp_filename , sess )
if img :
img = img . convert ( 'L' )
img . save ( tmp_filename , "jpeg" )
certainty = classifier . classify ( tmp_filename )
pos = certainty [ "positive" ]
ratings . append ( pos )
ratings . sort ( reverse = True )
ratings = ratings [: 5 ]
if len ( ratings ) == 0 :
return 0.001
return ratings [ 0 ] * 0.6 + sum ( ratings [ 1 :]) / len ( ratings [ 1 :]) * 0.4Теперь мы должны объединить все кусочки головоломки.
Во -первых, давайте инициализируем Tinder API с помощью нашего токена API. Затем мы открываем наш график классификации TensorFlow как сеанс TensorFlow, используя наш переподготовленный график и метки. Затем мы приносим людей поблизости и делаем прогноз вероятности.
В качестве небольшого бонуса я добавил, что вероятность составляет 1,2, если человек в Tinder поступит в тот же университет, что и я, так что я с большей вероятностью совпадаю с местными студентами.
Для всех людей, которые имеют прогнозируемый балл вероятности 0,8, я называю как, для всех других неприязни.
Я разработал сценарий для автоматической игры в течение следующих 2 часов после его запуска.
from likeliness_classifier import Classifier
import person_detector
import tensorflow as tf
from time import time
if __name__ == "__main__" :
token = "YOUR-API-TOKEN"
api = tinderAPI ( token )
detection_graph = person_detector . open_graph ()
with detection_graph . as_default ():
with tf . Session () as sess :
classifier = Classifier ( graph = "./tf/training_output/retrained_graph.pb" ,
labels = "./tf/training_output/retrained_labels.txt" )
end_time = time () + 60 * 60 * 2
while time () < end_time :
try :
persons = api . nearby_persons ()
pos_schools = [ "Universität Zürich" , "University of Zurich" , "UZH" ]
for person in persons :
score = person . predict_likeliness ( classifier , sess )
for school in pos_schools :
if school in person . schools :
print ()
score *= 1.2
print ( "-------------------------" )
print ( "ID: " , person . id )
print ( "Name: " , person . name )
print ( "Schools: " , person . schools )
print ( "Images: " , person . images )
print ( score )
if score > 0.8 :
res = person . like ()
print ( "LIKE" )
else :
res = person . dislike ()
print ( "DISLIKE" )
except Exception :
pass
classifier . close ()Вот и все! Теперь мы можем позволить нашему сценарию работать так долго, как нам нравится, и играть в Tinder, не злоупотребляя большим пальцем!
Если у вас есть вопросы или обнаружили ошибки, не стесняйтесь внести свой вклад в мой репозиторий GitHub.
MIT Лицензия
Copyright (C) 2018 Джоэл Барметлер
Настоящее разрешение предоставляется бесплатно любому лицу, получающему копию этого программного обеспечения и связанные с ними файлы документации («Программное обеспечение»), для работы в программном обеспечении без ограничений, включая, помимо прочего, права на использование, копирование, изменение, объединение , публиковать, распространять, сублиценс и/или продавать копии программного обеспечения и разрешить лицам, которым предоставлено программное обеспечение, при условии следующих условий:
Вышеуказанное уведомление об авторском праве и это уведомление о разрешении должно быть включено во все копии или существенные части программного обеспечения.
Программное обеспечение предоставляется «как есть», без гарантии любого рода, явного или подразумеваемого, включая, помимо прочего, гарантии товарной пригодности, пригодности для определенной цели и несоответствия. Ни в коем случае не будут нести ответственность авторов или владельцев авторских прав за любые претензии, ущерб или другую ответственность, будь то в действии контракта, деликте или иным образом, возникающим из или в связи с программным обеспечением или использования или других сделок в ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ.
Наймите нас: программное обеспечение Entwickler в Цюрихе!