Auto-Tander ถูกสร้างขึ้นเพื่อฝึก API โดยใช้ TensorFlow และ Python3 ที่เรียนรู้ความสนใจของคุณและเล่นเกมปัดไฟสำหรับคุณโดยอัตโนมัติ

ในเอกสารนี้ฉันจะอธิบายขั้นตอนต่อไปนี้ที่จำเป็นในการสร้างเครื่องวัดอัตโนมัติ:
Auto Tinder เป็นโครงการแนวคิดที่สร้างขึ้นอย่างหมดจดเพื่อความสนุกสนานและการศึกษา มันจะไม่ถูกทารุณกรรมที่จะทำร้ายใครหรือสแปมแพลตฟอร์ม ไม่ควรใช้สคริปต์อัตโนมัติด้วยตัวชี้วัดกับโปรไฟล์ Tinder ของคุณเนื่องจากพวกเขาละเมิดข้อกำหนดในการให้บริการของ Tinders อย่างแน่นอน
ฉันได้เขียนซอฟต์แวร์ชิ้นนี้ส่วนใหญ่ด้วยเหตุผลสองประการ:
ขั้นตอนแรกคือการค้นหาว่าแอป Tinder สื่อสารกับเซิร์ฟเวอร์แบ็กเอนด์ Tinders ได้อย่างไร เนื่องจาก Tinder นำเสนอพอร์ทัลเวอร์ชันเว็บนี่เป็นเรื่องง่ายเหมือนไปที่ Tinder.com เปิด chrome devtools และดูโปรโตคอลเครือข่ายอย่างรวดเร็ว
เนื้อหาที่แสดงในภาพด้านบนนั้นมาจากคำขอไปยัง https://api.gotinder.com/v2/recs/core ที่ทำเมื่อหน้า Landing Page 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
ด้วยการคัดลอกโทเค็นนี้และไปที่บุรุษไปรษณีย์เราสามารถตรวจสอบได้ว่าเราสามารถสื่อสารกับ Tinder API ได้อย่างอิสระด้วย URL ที่เหมาะสมและโทเค็น Auth ของเรา
ด้วยการคลิกผ่าน Tinders WebApp ฉันค้นพบจุดสิ้นสุด API ที่เกี่ยวข้องทั้งหมดอย่างรวดเร็ว:
| พิมพ์ | url | คำอธิบาย |
|---|---|---|
| รับ | /v2/recs/core | ส่งคืนรายชื่อผู้คนในบริเวณใกล้เคียง |
| รับ | /v2/โปรไฟล์? รวม = บัญชี%2Cuser | ส่งคืนข้อมูลทั้งหมดเกี่ยวกับโปรไฟล์ของคุณเอง |
| รับ | /v2/matches | ส่งคืนรายชื่อของทุกคนที่ตรงกับคุณ |
| รับ | /like/{user_id} | ชอบบุคคลที่มี user_id ที่กำหนด |
| รับ | /pass/{user_id} | ผ่านบุคคลที่มี user_id ที่กำหนด |
ลองเข้ารหัสกันเถอะ เราจะใช้ไลบรารีคำขอ Python เพื่อสื่อสารกับ API และเขียนคลาส Wrapper API รอบ ๆ เพื่อความสะดวก
ในทำนองเดียวกันเราเขียนคลาสบุคคลเล็ก ๆ ที่ใช้การตอบสนอง API จาก Tinder ซึ่งเป็นตัวแทนของบุคคลและเสนออินเทอร์เฟซพื้นฐานบางอย่างให้กับ Tinder API
เริ่มต้นด้วยชั้นเรียนบุคคล จะได้รับข้อมูล API วัตถุ Tinder-API และบันทึกข้อมูลที่เกี่ยวข้องทั้งหมดลงในตัวแปรอินสแตนซ์ มันจะนำเสนอคุณสมบัติพื้นฐานบางอย่างเช่น "Like" หรือ "ไม่ชอบ" ที่ร้องขอไปยัง 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 )Wrapper 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 เพื่อค้นหาผู้คนในบริเวณใกล้เคียงและดูโปรไฟล์ของพวกเขาหรือแม้แต่ชอบพวกเขาทั้งหมด แทนที่ Token-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() ต่อไปเราต้องการดาวน์โหลดภาพของผู้คนในบริเวณใกล้เคียงโดยอัตโนมัติซึ่งเราสามารถใช้สำหรับการฝึกอบรม AI ของเราโดยอัตโนมัติ ด้วย 'บาง' ฉันหมายถึงภาพ 1,500-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 ให้อัปเดตตำแหน่งของคุณตอนนี้และจากนั้นเพื่อรับคนใหม่
ตอนนี้เรามีภาพมากมายให้ทำงานด้วยเรามาสร้างตัวจําแนกที่เรียบง่ายและน่าเกลียด
มันจะวนซ้ำภาพทั้งหมดในโฟลเดอร์ "ไม่ได้จัดประเภท" ของเราและเปิดภาพในหน้าต่าง GUI โดยการคลิกขวาบุคคลเราสามารถทำเครื่องหมายบุคคลนั้นว่า "ไม่ชอบ" ในขณะที่การคลิกซ้ายทำเครื่องหมายบุคคลว่า "ชอบ" สิ่งนี้จะแสดงในชื่อไฟล์ในภายหลัง: 4TZ3KJLDFJ3482.jpg จะถูกเปลี่ยนชื่อเป็น 1_4TZ3KJLDFJ3482.jpg หากเราทำเครื่องหมายภาพเป็น "ชอบ" หรือ 0_4TZ3KJLDFJ3482.JPG เป็นอย่างอื่น ฉลากเช่น/ไม่ชอบถูกเข้ารหัสเป็น 1/0 ในตอนต้นของ fileenmae
มาใช้ tkinter เพื่อเขียน gui นี้อย่างรวดเร็ว:
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 Detensorflow Object API กับสถาปัตยกรรมเครือข่าย Mobilenet ซึ่งได้รับการปรับแต่งในชุดข้อมูล Coco ที่มีฉลากสำหรับ "บุคคล"
สคริปต์ของเราสำหรับการตรวจจับบุคคลมีสี่ส่วน:
คุณพบไฟล์. bp สำหรับกราฟ Tensorflow Mobilenet Coco ในที่เก็บ GitHub ของฉัน เปิดมันเป็นกราฟ 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_graphเราใช้หมอนสำหรับการจัดการภาพ เนื่องจาก TensorFlow ต้องการอาร์เรย์ Numpy Raw เพื่อทำงานกับข้อมูลเรามาเขียนฟังก์ชั่นเล็ก ๆ ที่แปลงภาพหมอนเป็นอาร์เรย์ 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 โดยใช้มันและส่งคืนข้อมูลทั้งหมดเกี่ยวกับคลาสที่ตรวจพบ (ประเภทวัตถุ) กล่องขอบเขตและคะแนน (ความมั่นใจว่าวัตถุถูกตรวจพบอย่างถูกต้อง)
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เป็นขั้นตอนสุดท้ายเราเขียนสคริปต์ที่วนซ้ำภาพทั้งหมดในโฟลเดอร์ "ไม่ได้จัดประเภท" ตรวจสอบว่าพวกเขามีฉลากที่เข้ารหัสในชื่อคัดลอกภาพในโฟลเดอร์ "จำแนก" โดยใช้ขั้นตอนการประมวลผลล่วงหน้าก่อนหน้านี้:
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 Script.py Script กับโมเดล InceptionV3
เรียกสคริปต์ในไดเรกทอรีรูทโครงการของคุณด้วยพารามิเตอร์ต่อไปนี้:
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% สำหรับชุดข้อมูลที่มีป้ายกำกับของฉัน แต่สิ่งนี้ขึ้นอยู่กับคุณภาพของข้อมูลอินพุตและการติดฉลากของคุณ
ผลลัพธ์ของกระบวนการฝึกอบรมคือโมเดล InceptionV3 ที่ได้รับการฝึกอบรมในไฟล์ "TF/Training_Output/Retraint_graph.pb" ตอนนี้เราต้องเขียนคลาสจําแนกที่ใช้น้ำหนักใหม่อย่างมีประสิทธิภาพในกราฟ Tensorflow เพื่อทำการทำนายการจำแนกประเภท
ลองเขียนคลาส classiFier ที่เปิดกราฟเป็นเซสชันและเสนอวิธี "จำแนก" ด้วยไฟล์รูปภาพที่ส่งคืนค่า 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 ตอนนี้เรามีตัวจําแนกของเราให้ขยายคลาส "บุคคล" จากก่อนหน้านี้และขยายด้วยฟังก์ชั่น "predict_likeliness" ที่ใช้อินสแตนซ์ตัวจําแนกเพื่อตรวจสอบว่าควรชอบบุคคลที่ได้รับหรือไม่
# 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 ()แค่ไหน! ตอนนี้เราสามารถปล่อยให้สคริปต์ของเราทำงานได้นานเท่าที่เราต้องการและเล่นเชื้อจุดไฟโดยไม่ใช้นิ้วโป้งของเรา!
หากคุณมีคำถามหรือพบข้อบกพร่องอย่าลังเลที่จะมีส่วนร่วมในที่เก็บ GitHub ของฉัน
ใบอนุญาต MIT
ลิขสิทธิ์ (c) 2018 Joel Barmettler
ได้รับอนุญาตโดยไม่เสียค่าใช้จ่ายสำหรับบุคคลใด ๆ ที่ได้รับสำเนาซอฟต์แวร์นี้และไฟล์เอกสารที่เกี่ยวข้อง ("ซอฟต์แวร์") เพื่อจัดการในซอฟต์แวร์โดยไม่มีการ จำกัด รวมถึง แต่ไม่ จำกัด เฉพาะสิทธิ์ในการใช้คัดลอกแก้ไขผสาน เผยแพร่แจกจ่าย sublicense และ/หรือขายสำเนาของซอฟต์แวร์และอนุญาตให้บุคคลที่ซอฟต์แวร์ได้รับการตกแต่งให้ทำเช่นนั้นภายใต้เงื่อนไขดังต่อไปนี้:
ประกาศลิขสิทธิ์ข้างต้นและประกาศการอนุญาตนี้จะรวมอยู่ในสำเนาทั้งหมดหรือส่วนสำคัญของซอฟต์แวร์
ซอฟต์แวร์มีให้ "ตามสภาพ" โดยไม่มีการรับประกันใด ๆ ไม่ว่าโดยชัดแจ้งหรือโดยนัยรวมถึง แต่ไม่ จำกัด เฉพาะการรับประกันความสามารถในการค้าการออกกำลังกายสำหรับวัตถุประสงค์เฉพาะและการไม่เข้าร่วม ไม่ว่าในกรณีใดผู้เขียนหรือผู้ถือลิขสิทธิ์จะต้องรับผิดชอบต่อการเรียกร้องความเสียหายหรือความรับผิดอื่น ๆ ไม่ว่าจะเป็นการกระทำของสัญญาการละเมิดหรืออื่น ๆ ซอฟต์แวร์.
จ้างเรา: ซอฟต์แวร์ Entwickler ในZürich!