Auto-Tinder a été créé pour former une API à l'aide de TensorFlow et Python3 qui apprend vos intérêts et joue automatiquement le jeu de balayage Tinder pour vous.

Dans ce document, je vais expliquer les étapes suivantes qui étaient nécessaires pour créer une auto-tinder:
Auto Tinder est un projet de concept purement créé à des fins de plaisir et d'éducation. Il ne sera jamais maltraité pour nuire à quiconque ou spammer la plate-forme. Les scripts automatique-Tinder ne doivent pas être utilisés avec votre profil Tinder car ils violent sûrement les conditions d'utilisation des Tinders.
J'ai écrit ce logiciel principalement sur deux raisons:
La première étape consiste à savoir comment l'application Tinder communique au serveur de backend Tinders. Étant donné que Tinder propose une version Web de son portail, c'est aussi simple que d'aller sur Tinder.com, ouvrant Chrome Devtools et jetez un œil rapide au protocole réseau.
Le contenu illustré dans l'image ci-dessus provenait d'une demande à https://api.gotinder.com/v2/recs/core qui est réalisée lorsque la page de destination de Tinder.com se charge. De toute évidence, Tinder possède une sorte d'API interne qu'ils utilisent pour communiquer entre le front et le backend.
Avec l'analyse du contenu de / Recs / Core , il devient clair que ce point de terminaison API renvoie une liste de profils d'utilisateurs de personnes à proximité.
Les données comprennent (entre de nombreux autres champs), les données suivantes:
{
"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" : []
}
}
]
}
}
Quelques choses sont très intéressantes ici (notez que j'ai changé toutes les données pour ne pas violer la confidentialité de cette personne) :
En analysant les en-têtes de contenu, nous trouvons rapidement nos clés API privées: X-Auth-token .
En copie ce jeton et en passant à Postman, nous pouvons valider que nous pouvons en effet communiquer librement avec l'API Tinder avec juste la bonne URL et notre jeton Auth.
En cliquant un peu sur Tinders WebApp, je découvre rapidement tous les points de terminaison API pertinents:
| Taper | URL | Description |
|---|---|---|
| OBTENIR | / V2 / Recs / Core | Renvoie une liste de personnes à proximité |
| OBTENIR | / v2 / profil? inclure = compty% 2CUSER | Renvoie toutes les informations sur votre propre profil |
| OBTENIR | / V2 / Matches | Renvoie une liste de toutes les personnes qui se sont associées |
| OBTENIR | / like / {user_id} | Aime la personne avec l'utilisateur donné_id |
| OBTENIR | / pass / {user_id} | Passe la personne avec l'utilisateur donné_id |
Alors passons dans le code. Nous utiliserons la bibliothèque Python Demandes pour communiquer avec l'API et écrire une classe de wrapper API autour de lui pour plus de commodité.
De même, nous écrivons une classe de petite personne qui prend la réponse de l'API de Tinder représentant une personne et offre quelques interfaces de base à l'API Tinder.
Commençons par la classe de personne. Il doit recevoir des données API, un objet Tinder-API et enregistrer toutes les données pertinentes dans les variables d'instance. Il doit en outre offrir des fonctionnalités de base comme "Like" ou "Alionke" qui font une demande à Tinder-API, ce qui nous permet d'utiliser commodément "Some_person.like ()" afin d'aimer un profil que nous trouvons intéressant.
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 )Notre emballage API n'est pas bien plus qu'une façon fantaisie d'appeler l'API Tinder à l'aide d'une classe:
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" ]))Nous pouvons maintenant utiliser l'API pour trouver des gens à proximité et jeter un œil à leur profil, ou même comme tous. Remplacez votre-API-Token par le X-Auth-token que vous avez trouvé dans la console Chrome Dev plus tôt.
if __name__ == "__main__" :
token = "YOUR-API-TOKEN"
api = tinderAPI ( token )
while True :
persons = api . nearby_persons ()
for person in persons :
print ( person )
# person.like() Ensuite, nous voulons télécharger automatiquement quelques images de personnes à proximité que nous pouvons utiliser pour former notre IA. Avec «certains», je veux dire comme 1500-2500 images.
Tout d'abord, étendons notre classe de personne avec une fonction qui nous permet de télécharger des images.
# 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 )Notez que j'ai ajouté des sommets aléatoires ici et là, simplement parce que nous serons probablement bloqués si nous spamz le CDN Tinder et téléchargeons de nombreuses photos en quelques secondes.
Nous écrivons tous les ID de profil des peuples dans un fichier appelé "Profils.txt". En scannant d'abord le document si une personne en particulier est déjà là, nous pouvons ignorer les personnes que nous avons déjà rencontrées, et nous nous assurons de ne pas classer les gens plusieurs fois (vous verrez plus tard pourquoi il s'agit d'un risque).
Nous pouvons désormais boucler les personnes à proximité et télécharger leurs images dans un dossier "non classé".
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 )Nous pouvons maintenant simplement démarrer ce script et le laisser fonctionner pendant quelques heures pour obtenir quelques images de profil Hundret de personnes à proximité. Si vous êtes un utilisateur de Tinder Pro, mettez à jour votre emplacement de temps en temps pour obtenir de nouvelles personnes.
Maintenant que nous avons un tas d'images avec lesquelles travailler, construisons un classificateur vraiment simple et laid.
Il doit simplement boucler sur toutes les images de notre dossier "non classé" et ouvrir l'image dans une fenêtre GUI. En cliquant avec le bouton droit sur une personne, nous pouvons marquer la personne comme "n'aime pas", tandis qu'un clic gauche marque la personne comme "comme". Cela sera représenté dans le nom de fichier ultérieurement: 4tz3kjldfj3482.jpg sera renommé 1_4tz3kjldfj3482.jpg si nous marquons l'image comme "comme", ou 0_4tz3kjldfj3482.jpg autrement. L'étiquette comme / aversion est codée comme 1/0 au début de la Filenmae.
Utilisons Tkinter pour écrire cette interface graphique rapidement:
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 ()Nous chargeons toutes les images non classifiées dans la liste "non classifiée_images", ouvrons une fenêtre Tkinter, emballons la première image en appelant next_img () et redimensionnez l'image pour s'adapter à l'écran. Ensuite, nous enregistrons deux clics, les boutons de la souris gauche et droite, et appelons les fonctions positives / négatives qui renomment les images en fonction de leur étiquette et affichent l'image suivante.
Laid mais efficace.
Pour la prochaine étape, nous devons apporter nos données d'image dans un format qui nous permet de faire une classification. Il y a quelques difficultés que nous devons considérer compte tenu de notre ensemble de données.
Nous combattons ces défis par:

La première partie est aussi simple que d'utiliser un oreiller pour ouvrir notre image et la convertir en niveaux de gris. Pour la deuxième partie, nous utilisons l'API de détection d'objets TensorFlow avec l'architecture du réseau MobileNet, pré-entraîné sur l'ensemble de données CoCo qui contient également une étiquette pour "personne".
Notre script pour la détection des personnes a quatre parties:
Vous trouvez le fichier .bp pour le graphique TensorFlow Mobilenet Coco dans mon référentiel GitHub. Ouvrez-le comme un graphique 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_graphNous utilisons un oreiller pour la manipulation d'image. Étant donné que Tensorflow a besoin de tableaux Numpy bruts pour fonctionner avec les données, écrivons une petite fonction qui convertit les images d'oreiller en tableaux 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 fonction suivante prend une image et un graphique TensorFlow, exécute une session TensorFlow à l'aide et renvoie toutes les informations sur les classes détectées (types d'objets), les boîtes de délimitation et les scores (certitude que l'objet a été détecté correctement).
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_dictLa dernière étape consiste à écrire une fonction qui prend un chemin d'image, l'ouvre à l'aide d'un oreiller, appelle l'interface de l'API de détection d'objet et culte l'image en fonction de la boîte de délimitation des personnes détectées.
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 NoneDans la dernière étape, nous écrivons un script qui boucle sur toutes les images du dossier "non classé", vérifie s'ils ont une étiquette codée dans le nom copie l'image dans le dossier selon "classifié" avec l'application des étapes de prétraitement développées précédemment:
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" )Lorsque nous exécutons ce script, toutes les images étiquetées sont en cours de traitement et déplacées dans des sous-dossiers correspondants dans le répertoire "classifié".
Pour la pièce de recyclage, nous allons simplement utiliser le script TensorFlow RettraThy.py avec le modèle IMCEPTINGV3.
Appelez le script dans le répertoire racine de votre projet avec les paramètres suivants:
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.001L'apprentissage dure environ 15 minutes sur un GTX 1080 Ti, avec une précision finale d'environ 80% pour mon ensemble de données étiqueté, mais cela dépend fortement de la qualité de vos données d'entrée et de votre étiquetage.
Le résultat du processus de formation est un modèle recyclé en INCECCEPV3 dans le fichier "TF / Training_Output / Rettrained_graph.pb". Nous devons maintenant écrire une classe de classificateur qui utilise efficacement les nouveaux poids dans le graphique TensorFlow pour faire une prédiction de classification.
Écrivons une classe de classificateur qui ouvre le graphique en tant que session et propose une méthode "classifier" avec un fichier image qui renvoie un dict avec des valeurs de certitude correspondant à nos étiquettes "positives" et "négatives".
La classe prend en entrée à la fois le chemin d'accès au graphique ainsi que le chemin d'accès au fichier d'étiquette, tous deux assis dans notre dossier "TF / Training_Output /". Nous développons des fonctions d'assistance pour convertir un fichier image en un tenseur que nous pouvons alimenter en notre graphique, une fonction d'assistance pour charger le graphique et les étiquettes et une petite fonction importante pour fermer notre graphique après avoir terminé l'utilisation.
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 Maintenant que nous avons notre classificateur en place, étendons la classe "Person" de plus tôt et étendons-la avec une fonction "Predict_likeness" qui utilise une instance de classificateur pour vérifier si une personne donnée doit être appréciée ou non.
# 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.4Maintenant, nous devons rassembler toutes les pièces de puzzle.
Tout d'abord, initialisons l'API Tinder avec notre jeton API. Ensuite, nous ouvrons notre graphique TensorFlow de classification en tant que session TensorFlow en utilisant notre graphique et les étiquettes recouverts. Ensuite, nous allons chercher des personnes à proximité et faisons une prédiction de probabilité.
En prime, j'ai ajouté une probabilité de 1,2 si la personne sur Tinder se rend dans la même université que moi, afin que je sois plus susceptible de correspondre aux étudiants locaux.
Pour toutes les personnes qui ont un score de probabilité prévu de 0,8, j'appelle un semblable, pour toutes les autres averses.
J'ai développé le script pour jouer automatiquement pour les 2 prochaines heures après le début.
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 ()C'est ça! Nous pouvons maintenant laisser notre script fonctionner aussi longtemps que nous aimons et jouons à Tinder sans abuser de notre pouce!
Si vous avez des questions ou trouvé des bugs, n'hésitez pas à contribuer à mon référentiel GitHub.
Licence MIT
Copyright (c) 2018 Joel Barmettler
L'autorisation est accordée gratuitement par la présente à toute personne qui obtient une copie de ce logiciel et des fichiers de documentation associés (le "logiciel"), pour traiter le logiciel sans restriction, y compris sans limiter les droits d'utilisation, de copier, de modifier, de fusionner , publier, distribuer, sous-licencier et / ou vendre des copies du logiciel, et pour permettre aux personnes à qui le logiciel est fourni pour le faire, sous réserve des conditions suivantes:
L'avis de droit d'auteur ci-dessus et le présent avis d'autorisation sont inclus dans toutes les copies ou des parties substantielles du logiciel.
Le logiciel est fourni "tel quel", sans garantie d'aucune sorte, express ou implicite, y compris, mais sans s'y limiter, les garanties de qualité marchande, d'adéquation à un usage particulier et de non-contrefaçon. En aucun cas, les auteurs ou les titulaires de droits d'auteur ne seront pas responsables de toute réclamation, dommage ou autre responsabilité, que ce soit dans une action de contrat, de délit ou autre, découlant de, hors du logiciel ou de l'utilisation ou d'autres transactions dans le LOGICIEL.
Embauchez-nous: Logiciel Entwickler à Zurich!