O tocador automático foi criado para treinar uma API usando o Tensorflow e o Python3 que aprende seus interesses e reproduz automaticamente o jogo do Tinder Swiping para você.

Neste documento, vou explicar as seguintes etapas necessárias para criar um rebelde automático:
O Auto Tinder é um projeto conceitual criado puramente para fins divertidos e educacionais. Nunca será abusado prejudicar ninguém ou spam a plataforma. Os scripts de retenção automática não devem ser usados com o perfil do seu Tinder, pois certamente violam os Termos de Serviço do Tinders.
Eu escrevi este software principalmente de dois motivos:
A primeira etapa é descobrir como o aplicativo Tinder se comunica com o Tinders Backend Server. Como o Tinder oferece uma versão da Web do seu portal, isso é tão fácil quanto ir ao Tinder.com, abrir o Chrome Devtools e dar uma olhada rápida no protocolo de rede.
O conteúdo mostrado na figura acima era de uma solicitação para https://api.gotinder.com/v2/recs/core que é fabricada quando a página de destino do Tinder.com está carregando. Claramente, o Tinder tem algum tipo de API interna que eles estão usando para se comunicar entre o front e o back-end.
Ao analisar o conteúdo do /Recs /Core , fica claro que esse endpoint da API retorna uma lista de perfis de usuário de pessoas próximas.
Os dados incluem (entre muitos outros campos), os seguintes dados:
{
"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" : []
}
}
]
}
}
Algumas coisas são muito interessantes aqui (observe que eu mudei todos os dados para não violar a privacidade dessas pessoas) :
Ao analisar os cabeçalhos de conteúdo, encontramos rapidamente nossas chaves de API privadas: X-Auth-Token .
Ao copiar esse token e ir ao Postman, podemos validar que podemos realmente nos comunicar livremente com a API do Tinder com o URL certo e nosso token de autenticação.
Ao clicar um pouco através do Tinders WebApp, descobri rapidamente todos os pontos de extremidade da API relevantes:
| Tipo | Url | Descrição |
|---|---|---|
| PEGAR | /v2/recs/núcleo | Retorna uma lista de pessoas próximas |
| PEGAR | /v2/perfil? incluir = conta%2cuser | Retorna todas as informações sobre seu próprio perfil |
| PEGAR | /V2/Matches | Retorna uma lista de todas as pessoas que compararam com você |
| PEGAR | /como/{user_id} | Gosta da pessoa com o user_id dado |
| PEGAR | /pass/{user_id} | Passa a pessoa com o user_id dado |
Então, vamos entrar no código. Usaremos a Biblioteca de solicitações do Python para se comunicar com a API e escrever uma classe de invólucro de API em torno dela por conveniência.
Da mesma forma, escrevemos uma classe de uma pequena pessoa que recebe a resposta da API do Tinder representando uma pessoa e oferece algumas interfaces básicas à API do Tinder.
Vamos começar com a aula de pessoa. Ele deve receber dados da API, um objeto Tinder-API e salvar todos os dados relevantes em variáveis de instância. Ele deve oferecer ainda alguns recursos básicos como "como" ou "não gostar" que solicitem o Tinder-API, o que nos permite usar convenientemente "algum_person.like ()" para gostar de um perfil que achamos interessantes.
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 )Nosso invólucro de API não é muito mais do que uma maneira chique de chamar a API do Tinder usando uma 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" ]))Agora podemos usar a API para encontrar pessoas por perto e dar uma olhada no perfil deles, ou mesmo como todas elas. Substitua o seu Api-Token pelo x-Auth-Token que você encontrou no console do 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() Em seguida, queremos baixar automaticamente algumas imagens de pessoas próximas que podemos usar para treinar nossa IA. Com 'alguns', quero dizer como 1500-2500 imagens.
Primeiro, vamos estender nossa classe de pessoa com uma função que nos permite baixar imagens.
# 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 )Observe que adicionei alguns sono aleatórios aqui e ali, apenas porque provavelmente estaremos bloqueados se spam o Tinder CDN e baixarmos muitas fotos em apenas alguns segundos.
Escrevemos todos os IDs do perfil das pessoas em um arquivo chamado "perfis.txt". Ao digitalizar primeiro o documento se uma pessoa em particular já está lá, podemos pular pessoas que já encontramos e garantimos que não classifiquemos as pessoas várias vezes (você verá mais adiante por que isso é um risco).
Agora podemos apenas atravessar pessoas próximas e baixar suas imagens em uma pasta "não classificada".
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 )Agora podemos simplesmente iniciar esse script e deixá -lo correr por algumas horas para obter algumas imagens de perfil de Hundret de pessoas próximas. Se você é um usuário do Tinder Pro, atualize sua localização de vez em quando para obter novas pessoas.
Agora que temos várias imagens para trabalhar, vamos construir um classificador realmente simples e feio.
Ele apenas percorre todas as imagens em nossa pasta "não classificada" e abrirá a imagem em uma janela da GUI. Ao clicar com o botão direito do mouse, podemos marcar a pessoa como "antipatia", enquanto um clique esquerdo marca a pessoa como "como". Isso será representado no nome do arquivo posteriormente em: 4tz3kjldfj3482.jpg será renomeado para 1_4tz3kjldfj3482.jpg se marcarmos a imagem como "como" ou 0_4tz3kjldfj3482.jpg de outra forma. O rótulo como/não gosta é codificado como 1/0 no início do arquivo.
Vamos usar o TKINER para escrever esta GUI rapidamente:
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 ()Carregamos todas as imagens não classificadas na lista "UnncloSiFied_Images", abrimos uma janela Tkinter, empacotamos a primeira imagem chamando o próximo_IMG () e redimensionamos a imagem para caber na tela. Em seguida, registramos dois cliques, botões da esquerda e do mouse direito e chamamos as funções de positivo/negativo que renomeiam as imagens de acordo com a gravadora e mostramos a próxima imagem.
Feio, mas eficaz.
Para a próxima etapa, precisamos trazer nossos dados de imagem para um formato que nos permita fazer uma classificação. Existem algumas dificuldades que devemos considerar, dado o nosso conjunto de dados.
Nós combatemos esses desafios:

A primeira parte é tão fácil quanto usar travesseiro para abrir nossa imagem e convertê -la em escala de escala. Para a segunda parte, usamos a API de detecção de objeto Tensorflow com a arquitetura de rede MobileNet, pré -treinada no conjunto de dados Coco que também contém um rótulo para "pessoa".
Nosso script para detecção de pessoas tem quatro partes:
Você encontra o arquivo .bp para o tensorflow mobilenet coco gráfico no meu repositório do GitHub. Vamos abri -lo como um gráfico de 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 travesseiro para manipulação de imagem. Como o Tensorflow precisa de matrizes numpy cruas para trabalhar com os dados, vamos escrever uma pequena função que converte imagens de travesseiros em matrizes 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 )A próxima função pega uma imagem e um gráfico de tensorflow, executa uma sessão de tensorflow usando -a e retorne todas as informações sobre as classes detectadas (tipos de objetos), caixas e pontuações delimitadoras (certeza de que o objeto foi detectado corretamente).
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_dictA última etapa é escrever uma função que segue um caminho de imagem, abre -a usando travesseiro, chama a interface da API de detecção de objeto e colhe a imagem de acordo com a caixa delimitadora das pessoas 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 última etapa, escrevemos um script que atravessa todas as imagens na pasta "não classificada", verifica se elas têm uma etiqueta codificada no nome copia a imagem na pasta "classificada" de acordo com a aplicação das etapas de pré -processamento previamente desenvolvidas:
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" )Quando executamos esse script, todas as imagens rotuladas estão sendo processadas e transferidas para as subpastas correspondentes no diretório "classificado".
Para a parte de reciclagem, usaremos o script TensorFlows RETRAIN.PY com o modelo InCceptionV3.
Ligue para o script no diretório raiz do seu projeto com os seguintes 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.001O aprendizado leva cerca de 15 minutos em um GTX 1080 TI, com uma precisão final de cerca de 80% para o meu conjunto de dados rotulado, mas isso depende muito da qualidade dos seus dados de entrada e da sua rotulagem.
O resultado do processo de treinamento é um modelo de INCCECTIONV3 RETRIENTE no arquivo "TF/TREINA_OUTPUT/RETRIENHELED_GRAPH.PB". Agora devemos escrever uma classe de classificador que use com eficiência os novos pesos no gráfico do tensorflow para fazer uma previsão de classificação.
Vamos escrever uma classe de classificador que abre o gráfico como uma sessão e oferece um método "classificar" com um arquivo de imagem que retorna um ditado com valores de certeza que correspondem aos nossos rótulos "positivos" e "negativos".
A classe toma como entrada tanto o caminho para o gráfico quanto o caminho para o arquivo de etiqueta, ambos na nossa pasta "tf/treinamento_output/". Desenvolvemos funções auxiliares para converter um arquivo de imagem em um tensor que podemos alimentar em nosso gráfico, uma função auxiliar para carregar o gráfico e os rótulos e uma pequena função importante para fechar nosso gráfico depois que terminarmos de usá -lo.
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 Agora que temos nosso classificador em vigor, vamos estender a classe "Pessoa" de anterior e estendê -la com uma função "previc_likeliness" que usa uma instância de classificador para verificar se uma determinada pessoa deve ser apreciada ou não.
# 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.4Agora temos que reunir todas as peças do quebra -cabeça.
Primeiro, vamos inicializar a API do Tinder com nosso token da API. Em seguida, abrimos nosso gráfico de classificação Tensorflow como uma sessão de tensorflow usando nosso gráfico e etiquetas de treinado. Então, buscamos pessoas por perto e fazemos uma previsão de probabilidade.
Como um pouco de bônus, adicionei um multipliente de probabilidade de 1.2 se a pessoa em Tinder for para a mesma universidade que eu, para que seja mais provável que eu corresponda aos estudantes locais.
Para todas as pessoas que têm uma pontuação de probabilidade prevista de 0,8, eu chamo de um, para todos os outros de uma aversão.
Desenvolvi o script para jogar automaticamente as próximas 2 horas após o início.
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 ()É isso! Agora podemos deixar nosso script correr pelo tempo que gostarmos e jogar Tinder sem abusar do polegar!
Se você tiver dúvidas ou encontrar bugs, fique à vontade para contribuir com o meu repositório do GitHub.
MIT Licença
Copyright (c) 2018 Joel Barmettler
É concedida permissão, gratuitamente, a qualquer pessoa que obtenha uma cópia deste software e arquivos de documentação associados (o "software"), para lidar com o software sem restrição, incluindo, sem limitação, os direitos de uso, copiar, modificar, mesclar .
O aviso de direitos autorais acima e este aviso de permissão devem ser incluídos em todas as cópias ou em partes substanciais do software.
O software é fornecido "como está", sem garantia de qualquer tipo, expresso ou implícito, incluindo, entre outros, as garantias de comercialização, aptidão para uma finalidade específica e não innoculação. Em nenhum caso os autores ou detentores de direitos autorais serão responsabilizados por qualquer reclamação, danos ou outro passivo, seja em uma ação de contrato, delito ou não, decorrente de, fora ou em conexão com o software ou o uso ou outras negociações nos Software.
Contrate -nos: software Entwickler em Zurique!