Auto-Tinder fue creado para entrenar una API usando TensorFlow y Python3 que aprende sus intereses y juega automáticamente el juego de deslizamiento Tinder para usted.

En este documento, voy a explicar los siguientes pasos necesarios para crear auto-tinder:
Auto Tinder es un proyecto conceptual creado puramente con fines divertidos y educativos. Nunca se abusará de dañar a nadie o enviar spam a la plataforma. Los scripts de tindería automática no deben usarse con su perfil de Tinder, ya que seguramente violan los términos de servicio de Tinders.
He escrito este software principalmente de dos razones:
El primer paso es averiguar cómo se comunica la aplicación Tinder con el servidor de backend de Tinders. Dado que Tinder ofrece una versión web de su portal, esto es tan fácil como ir a Tinder.com, abrir Chrome DevTools y echar un vistazo rápido al protocolo de red.
El contenido que se muestra en la imagen de arriba fue de una solicitud a https://api.gotinder.com/v2/recs/core que se realiza cuando se carga la página de destino de Tinder.com. Claramente, Tinder tiene algún tipo de API interna que están utilizando para comunicarse entre el delantero y el backend.
Al analizar el contenido de /recs /núcleo , queda claro que este punto final de la API devuelve una lista de perfiles de usuarios de personas cercanas.
Los datos incluyen (entre muchos otros campos), los siguientes datos:
{
"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" : []
}
}
]
}
}
Algunas cosas son muy interesantes aquí (tenga en cuenta que cambié todos los datos para no violar la privacidad de esta persona) :
Con el análisis de los encabezados de contenido, rápidamente encontramos nuestras claves de API privadas: X-Auth-Token .
Al copiar esta ficha y ir a Postman, podemos validar que realmente podemos comunicarnos libremente con la API de Tinder con la URL correcta y nuestra ficha de autenticación.
Con hacer clic un poco a través de Tinders WebApp, descubro rápidamente todos los puntos finales de API relevantes:
| Tipo | Url | Descripción |
|---|---|---|
| CONSEGUIR | /v2/recs/core | Devuelve una lista de personas cercanas |
| CONSEGUIR | /v2/perfil? include = cuenta%2cuser | Devuelve toda la información sobre su propio perfil |
| CONSEGUIR | /V2/Partidos | Devuelve una lista de todas las personas que han emparejado con usted |
| CONSEGUIR | /me gusta/{user_id} | Le gusta a la persona con el user_id dado |
| CONSEGUIR | /pass/{user_id} | Pasa a la persona con el user_id dado |
Así que entremos en el código. Utilizaremos la Biblioteca de solicitudes de Python para comunicarse con la API y escribir una clase de envoltorio API a su alrededor por conveniencia.
Del mismo modo, escribimos una clase de persona pequeña que toma la respuesta de la API de Tinder que representa a una persona y ofrece algunas interfaces básicas a la API de Tinder.
Comencemos con la clase de la persona. Recibirá datos API, un objeto Tinder-API y guardará todos los datos relevantes en variables de instancia. Además, ofrecerá algunas características básicas como "como" o "disgusto" que hagan una solicitud al Tinder-API, que nos permite usar convenientemente "Some_person.ike ()" para que le guste un perfil que encontramos interesante.
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 )Nuestro envoltorio API no es mucho más que una forma elegante de llamar a la API de Tinder usando una clase:
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" ]))Ahora podemos usar la API para encontrar personas cerca y echar un vistazo a su perfil, o incluso como todos ellos. Reemplace su-api-token con el X-Auth-token que encontró en la consola Chrome Dev anteriormente.
if __name__ == "__main__" :
token = "YOUR-API-TOKEN"
api = tinderAPI ( token )
while True :
persons = api . nearby_persons ()
for person in persons :
print ( person )
# person.like() A continuación, queremos descargar automáticamente algunas imágenes de personas cercanas que podemos usar para capacitar a nuestra IA. Con 'Some', me refiero a imágenes de 1500-2500.
Primero, ampliemos nuestra clase de persona con una función que nos permite descargar imágenes.
# 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 )Tenga en cuenta que agregué algunos duros aleatorios aquí y allá, solo porque es probable que nos bloqueen si spam el Tinder CDN y descargamos muchas imágenes en solo unos segundos.
Escribimos todas las ID de perfil de las personas en un archivo llamado "Profiles.txt". Al escanear primero el documento si una persona en particular ya está allí, podemos omitir a las personas que ya encontramos, y nos aseguramos de no clasificar a las personas varias veces (verá más tarde por qué esto es un riesgo).
Ahora podemos recurrir a personas cercanas y descargar sus imágenes en una carpeta "no clasificada".
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 )Ahora podemos simplemente comenzar este script y dejar que se ejecute durante unas horas para obtener algunas imágenes de perfil de Hundret de personas cercanas. Si es un usuario de Tinder Pro, actualice su ubicación de vez en cuando para obtener nuevas personas.
Ahora que tenemos un montón de imágenes para trabajar, creemos un clasificador realmente simple y feo.
Simplemente recorrerá todas las imágenes en nuestra carpeta "no clasificada" y abrirá la imagen en una ventana de GUI. Al hacer clic con el botón derecho en una persona, podemos marcar a la persona como "disgusto", mientras que un clic izquierdo marca a la persona como "similar". Esto se representará en el nombre de archivo más adelante: 4tz3kjldfj3482.jpg se renombrará a 1_4tz3kjldfj3482.jpg si marcamos la imagen como "similar", o 0_4tz3kjldfj3482.jpg de lo contrario. La etiqueta como/disgusto se codifica como 1/0 al comienzo del filenmae.
Usemos Tkinter para escribir esta GUI rápidamente:
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 ()Cargamos todas las imágenes no clasificadas en la lista "Unclassified_images", abrimos una ventana Tkinter, empacamos la primera imagen llamando a Next_img () y cambia el tamaño de la imagen para que se ajuste a la pantalla. Luego, registramos dos clics, los botones del mouse de izquierda y derecha, y llamamos a las funciones positivas/negativas que renombran las imágenes de acuerdo con su etiqueta y muestran la siguiente imagen.
Feo pero efectivo.
Para el siguiente paso, necesitamos llevar nuestros datos de imagen a un formato que nos permita hacer una clasificación. Hay algunas dificultades que debemos considerar dado nuestro conjunto de datos.
Combatimos estos desafíos por:

La primera parte es tan fácil como usar la almohada para abrir nuestra imagen y convertirla a Greyscale. Para la segunda parte, utilizamos la API de detección de objetos TensorFlow con la arquitectura de la red MobileNet, previamente en el conjunto de datos de Coco que también contiene una etiqueta para "persona".
Nuestro guión para la detección de personas tiene cuatro partes:
Encuentra el archivo .bp para el gráfico TensorFlow MobileNet Coco en mi repositorio de GitHub. Abramos como un gráfico TensorFlow:
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_graphUsamos almohada para la manipulación de imágenes. Dado que TensorFlow necesita matrices numpy sin procesar para trabajar con los datos, escribamos una función pequeña que convierta las imágenes de almohadas en matrices 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 )La siguiente función toma una imagen y un gráfico de flujo de tensor, ejecuta una sesión de flujo de tensor utilizándola y devuelve todas las informaciones sobre las clases detectadas (tipos de objetos), cuadros delimitadores y puntajes (certeza de que el objeto se detectó correctamente).
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_dictEl último paso es escribir una función que tome una ruta de imagen, la abre usando almohada, llama a la interfaz API de detección de objetos y cultiva la imagen de acuerdo con el cuadro delimitador de personas detectadas.
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 NoneComo último paso, escribimos un script que brote sobre todas las imágenes en la carpeta "no clasificada", verifica si tienen una etiqueta codificada en el nombre copia la imagen en la carpeta según "clasificada" con la aplicación de los pasos de preprocesamiento desarrollados previamente:
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" )Cuando ejecutamos este script, todas las imágenes etiquetadas se están procesando y se mueven en las subcarpetas correspondientes en el directorio "clasificado".
Para la parte de reentrenamiento, solo usaremos el script de entrenamiento de tensorflows con el modelo InceptionV3.
Llame al script en su directorio raíz de proyecto con los siguientes parámetros:
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.001El aprendizaje lleva aproximadamente 15 minutos en un GTX 1080 Ti, con una precisión final de aproximadamente el 80% para mi conjunto de datos etiquetado, pero esto depende en gran medida de la calidad de sus datos de entrada y su etiquetado.
El resultado del proceso de capacitación es un modelo InceptionV3 reentrenado en el archivo "TF/Training_output/CoinTrained_Graph.pb". Ahora debemos escribir una clase de clasificador que use eficientemente los nuevos pesos en el gráfico TensorFlow para hacer una predicción de clasificación.
Escribamos una clase de clasificador que abre el gráfico como una sesión y ofrece un método de "clasificar" con un archivo de imagen que devuelve un dict con valores de certeza que coincidan con nuestras etiquetas "positivas" y "negativas".
La clase toma como entrada tanto la ruta al gráfico como la ruta al archivo de la etiqueta, ambas en nuestra carpeta "TF/Training_output/". Desarrollamos funciones auxiliares para convertir un archivo de imagen en un tensor que podamos alimentar en nuestro gráfico, una función de ayuda para cargar el gráfico y las etiquetas y una pequeña función importante para cerrar nuestro gráfico después de que terminemos de usarlo.
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 Ahora que tenemos nuestro clasificador en su lugar, ampliemos la clase de "persona" de antes y lo extendamos con una función de "predicción_likeliness" que usa una instancia de clasificador para verificar si una persona determinada debe ser querida o no.
# 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.4Ahora tenemos que unir todas las piezas del rompecabezas.
Primero, inicialicemos la API de Tinder con nuestro token API. Luego, abrimos nuestro gráfico de clasificación TensorFlow como una sesión de flujo de tensor utilizando nuestro gráfico y etiquetas capacitadas. Luego, buscamos personas cerca y hacemos una predicción probable.
Como un pequeño bono, agregué un multiplicador probable de 1.2 si la persona en Tinder va a la misma universidad que yo, por lo que es más probable que coincida con los estudiantes locales.
Para todas las personas que tienen un puntaje probable previsto de 0.8, llamo a un me gusta, para todos los demás a un disgusto.
Desarrollé el script para jugar automáticamente durante las próximas 2 horas después de que se inicie.
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 ()¡Eso es todo! ¡Ahora podemos dejar que nuestro guión funcione durante el tiempo que quiera y reproduzcamos Tinder sin abusar de nuestro pulgar!
Si tiene preguntas o errores encontrados, no dude en contribuir a mi repositorio de GitHub.
Licencia de MIT
Copyright (c) 2018 Joel Barmettler
El permiso se otorga, de forma gratuita, a cualquier persona que obtenga una copia de este software y archivos de documentación asociados (el "software"), para tratar el software sin restricción, incluidos los derechos de usar, copiar, modificar, modificar, modificar, fusionar , publique, distribuya, sublicence y venda copias del software, y para permitir a las personas a las que se proporciona el software para hacerlo, sujeto a las siguientes condiciones:
El aviso de derechos de autor anterior y este aviso de permiso se incluirán en todas las copias o porciones sustanciales del software.
El software se proporciona "tal cual", sin garantía de ningún tipo, expresa o implícita, incluidas, entre otros, las garantías de comerciabilidad, idoneidad para un propósito particular y no infracción. En ningún caso los autores o titulares de derechos de autor serán responsables de cualquier reclamo, daños u otra responsabilidad, ya sea en una acción de contrato, agravio o de otro tipo, derivado de o en relación con el software o el uso u otros tratos en el SOFTWARE.
Contrate de nosotros: ¡Software Entwickler en Zürich!