Auto-Tinder-訓練AI玩打火機刷卡遊戲

  • 2019 年 10 月 31 日
  • 筆記

在本文檔中,將解釋創建自動綁定所需的以下步驟:

  • 分析tinder網頁以找出tinder內部API調用的內容,在Postman中重構API調用並分析其內容
  • 在python中建立一個API包裝器類別,使用Tinder API進行喜歡/不喜歡/比賽等。
  • 下載一堆附近人的影像
  • 編寫簡單的滑鼠單擊分類器來標記我們的影像
  • 開發一個使用tensorflow對象檢測API的預處理器以僅裁剪影像中的人物
  • 訓練Inceptionv3(一種深度卷積神經網路),以學習分類數據
  • 將分類器與tinder API包裝器結合使用播放tinder

https://www.getpostman.com/

步驟0:動機和免責聲明

Auto Tinder是一個純粹出於娛樂和教育目的而創建的概念項目。絕不能濫用它來傷害任何人或向平台發送垃圾郵件。自動綁定腳本不應與您的綁定文件一起使用,因為它們肯定違反了綁定服務條款。

編寫此軟體的原因主要有兩個:

  • 因為可以,創建它很有趣:)
  • 想弄清楚一個AI是否真的能夠了解我的另一性,並成為我可靠的左右滑動夥伴。

步驟1:分析Tinder API

第一步是找出tinder應用程式如何與tender後端伺服器進行通訊。由於tinder提供其門戶網站的網路版本,因此與訪問tinder.com一樣簡單,它可以打開chrome devtools並快速查看網路協議。

上圖中顯示的內容是從請求發送到鏈接的請求,該請求是在tinder.com著陸頁載入時發出的。顯然,tinder具有某種內部API,正在使用它們在前端和後端之間進行通訊。

https://api.gotinder.com/v2/recs/core

通過分析/ 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": []                  }              }          ]      }  }

這裡有幾件事非常有趣(請注意,更改了所有數據以不侵犯此人的隱私):

  • 所有影像均可公開訪問。如果複製影像URL並在私人窗口中打開它,它仍然會立即載入-這意味著tinder將所有用戶影像公開上傳到Internet,任何人都可以自由查看。
  • 通過API可訪問的原始照片具有極高的解析度。如果將照片上傳到tinder,將按比例縮小以適合應用程式內使用,但會將原始版本公開存儲在其伺服器上,任何人都可以訪問。
  • 即使您選擇「 show_gender_on_profile」,每個人仍然可以通過API查看性別(「性別」:1,其中1 =女人,0 =男人)
  • 如果連續向tinder API發送多個請求,則始終會得到不同的結果(例如,不同的配置文件)。因此,可以反覆地將此端點稱為「農場」一堆圖片,以後可以用來訓練神經網路。

通過分析內容標頭,我們可以快速找到私有API密鑰:X-Auth-Token。

通過複製此令牌並轉到Postman,可以驗證確實可以僅使用正確的URL和auth令牌與tinder API自由通訊。

通過一點點單擊tinders webapp,很快發現了所有相關的API端點:

第2步:在Python中構建API包裝器

因此進入程式碼。為了方便起見,將使用python Requests庫與API通訊並在其周圍編寫一個API包裝器類。

https://requests.kennethreitz.org/en/master/

同樣,編寫了一個小的Person類,該類從Tinder接收代表一個Person的API響應,並提供了一些與Tinder API的基本介面。

從Person類開始。它應接收API數據,tinder-api對象,並將所有相關數據保存到實例變數中。它還將提供一些基本功能,例如「喜歡」或「不喜歡」,這些請求向tinder-api發出了請求,這使能夠方便地使用「 some_person.like()」來喜歡發現有趣的配置文件。

import datetime  from geopy.geocoders import Nominatim    TINDER_URL = "https://api.gotinder.com"  geolocator = Nominatim(user_agent="auto-tinder")  PROF_FILE = "./images/unclassified/profiles.txt"    class Person(object):        def __init__(self, data, api):          self._api = api            self.id = data["_id"]          self.name = data.get("name", "Unknown")            self.bio = data.get("bio", "")          self.distance = data.get("distance_mi", 0) / 1.60934            self.birth_date = datetime.datetime.strptime(data["birth_date"], '%Y-%m-%dT%H:%M:%S.%fZ') if data.get(              "birth_date", False) else None          self.gender = ["Male", "Female", "Unknown"][data.get("gender", 2)]            self.images = list(map(lambda photo: photo["url"], data.get("photos", [])))            self.jobs = list(              map(lambda job: {"title": job.get("title", {}).get("name"), "company": job.get("company", {}).get("name")}, data.get("jobs", [])))          self.schools = list(map(lambda school: school["name"], data.get("schools", [])))            if data.get("pos", False):              self.location = geolocator.reverse(f'{data["pos"]["lat"]}, {data["pos"]["lon"]}')          def __repr__(self):          return f"{self.id}  -  {self.name} ({self.birth_date.strftime('%d.%m.%Y')})"          def like(self):          return self._api.like(self.id)        def dislike(self):          return self._api.dislike(self.id)

API包裝器不過是使用類調用tinder API的理想方式:

import requests    TINDER_URL = "https://api.gotinder.com"    class tinderAPI():        def __init__(self, token):          self._token = token        def profile(self):          data = requests.get(TINDER_URL + "/v2/profile?include=account%2Cuser", headers={"X-Auth-Token": self._token}).json()          return Profile(data["data"], self)        def matches(self, limit=10):          data = requests.get(TINDER_URL + f"/v2/matches?count={limit}", headers={"X-Auth-Token": self._token}).json()          return list(map(lambda match: Person(match["person"], self), data["data"]["matches"]))        def like(self, user_id):          data = requests.get(TINDER_URL + f"/like/{user_id}", headers={"X-Auth-Token": self._token}).json()          return {              "is_match": data["match"],              "liked_remaining": data["likes_remaining"]          }        def dislike(self, user_id):          requests.get(TINDER_URL + f"/pass/{user_id}", headers={"X-Auth-Token": self._token}).json()          return True        def nearby_persons(self):          data = requests.get(TINDER_URL + "/v2/recs/core", headers={"X-Auth-Token": self._token}).json()          return list(map(lambda user: Person(user["user"], self), data["data"]["results"]))

現在,可以使用API查找附近的人,並查看個人資料,甚至可以查看所有人。將API令牌替換為之前在chrome開發者控制台中找到的X-Auth-Token。

if __name__ == "__main__":      token = "YOUR-API-TOKEN"      api = tinderAPI(token)        while True:          persons = api.nearby_persons()          for person in persons:              print(person)              # person.like()

第3步:下載附近人的影像

接下來,要自動下載附近的人的一些影像,以用於訓練AI。「有些」是指1500-2500張影像。

首先,通過一個允許下載影像的函數來擴展Person類。

# 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+"rn")          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)

請注意,在這裡和那裡添加了一些隨機睡眠,這是因為如果向垃圾郵件CDN發送垃圾郵件並在短短几秒鐘內下載許多圖片,可能會被阻止。

將所有人員檔案ID寫入名為「 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用戶,請立即更新位置,然後結識新朋友。

步驟4:手動分類影像

現在有很多影像可以使用,構建一個非常簡單且醜陋的分類器。

它將僅循環遍歷「未分類」文件夾中的所有影像,並在GUI窗口中打開該影像。通過右鍵單擊某個人,可以將該人標記為「不喜歡」,而單擊滑鼠左鍵將該人標記為「喜歡」。這將在文件名後上表示:4tz3kjldfj3482.jpg將被更名為1_4tz3kjldfj3482.jpg如果紀念像為「像」,或0_4tz3kjldfj3482.jpg否則。標籤like / dislike在文件名的開頭編碼為1/0。

使用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()將第一張影像打包到其中,並調整影像的大小以適合螢幕。然後,單擊滑鼠左鍵和滑鼠右鍵兩次,並調用正/負函數,該函數會根據其標籤重命名影像並顯示下一張影像。

醜陋但有效。

第5步:開發預處理程式以僅裁剪影像中的人物

下一步,需要將影像數據轉換為允許分類的格式。給定數據集,必須考慮一些困難。

  • 數據集大小:數據集相對較小。考慮到影像的複雜性,處理+ -2000張影像,這被認為是非常少量的數據(高解析度的RGB影像)
  • 數據差異:這些圖片有時包含來自背後的人物,有時僅包含面孔,有時甚至根本沒有人物。
  • 數據噪音:大多數圖片不僅包含人物本身,而且通常包含周圍的環境,這可能分散四個AI的注意力。

通過以下方式應對這些挑戰:

  • 將影像轉換為灰度,以將AI必須學習的資訊量減少三倍(RGB到G)
  • 僅切出影像中實際包含該人的部分,僅此而已

第一部分就像使用「枕頭」打開影像並將其轉換為灰度圖一樣容易。對於第二部分,將 Tensorflow對象檢測API與mobilenet網路體系結構一起使用,在可可數據集上進行了預訓練,該數據集還包含「人」的標籤。

https://github.com/tensorflow/models/tree/master/research/object_detection

人檢測腳本包含四個部分:

第1部分:打開預訓練的mobilenet可可數據集作為Tensorflow圖

可以在Github存儲庫中找到tensorflow mobilenet可可圖的.bp文件。將其作為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

第2部分:以numpy數組的形式載入影像

使用枕頭進行影像處理。由於tensorflow需要原始的numpy數組來處理數據,因此編寫一個小函數將Pillow影像轉換為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)

第3部分:調用對象檢測API

下一個函數獲取影像和張量流圖,使用它運行一個張量流會話,並返回有關檢測到的類(對象類型),邊界框和得分(確定正確檢測到對象的確定性)的所有資訊。

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 

第4部分:將所有內容組合在一起以找到人

最後一步是編寫一個獲取影像路徑的函數,使用Pillow將其打開,調用對象檢測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

第5部分:將所有影像移動到分類文件夾中

最後一步,編寫一個腳本來循環遍歷「未分類」文件夾中的所有影像,並使用先前開發的預處理步驟檢查它們是否在名稱中具有編碼標籤,從而將影像複製到「已分類」文件夾中的影像中:

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")

每當運行此腳本時,所有標記的影像都將被處理並移入「分類」目錄中的相應子文件夾中。

步驟6:重新訓練inceptionv3並編寫分類器

對於重新訓練部分,將僅將tensorflows retrain.py 腳本與inceptionv3模型一起使用。

https://github.com/tensorflow/hub/blob/master/examples/image_retraining/retrain.py

使用以下參數在項目根目錄中調用腳本:

python retrain.py --bottleneck_dir=tf/training_data/bottlenecks --model_dir=tf/training_data/inception --summaries_dir=tf/training_data/summaries/basic --output_graph=tf/training_output/retrained_graph.pb --output_labels=tf/training_output/retrained_labels.txt --image_dir=./images/classified --how_many_training_steps=50000 --testing_percentage=20 --learning_rate=0.001

在GTX 1080 ti上,學習大約需要15分鐘,對於標記的數據集,最終精度約為80%,但這在很大程度上取決於輸入數據和標記的品質。

訓練過程的結果是「 tf / training_output / retrained_graph.pb」文件中的重新訓練的inceptionV3模型。現在必須編寫一個分類器類,該類可以有效地使用張量流圖中的新權重進行分類預測。

寫一個Classifier-Class,它以會話形式打開圖形,並提供帶有影像文件的「 classify」方法,該影像文件將返回具有與標籤「 positive」和「 negative」相匹配的確定性值的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

第7步:使用所有這些內容自動播放Tinder

現在已經有了分類器,從前面擴展「 Person」類,並使用「 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

現在,必須將所有拼圖組合在一起。

首先,使用api令牌初始化tinder API。然後使用重新訓練的圖和標籤將分類張量流圖打開為張量流會話。然後獲取附近的人員並進行可能性預測。

作為一個小小的獎勵,如果Tinder上的人和我上同一所大學,添加了1.2的似然倍數,這樣就更有可能與本地學生匹配。

對於所有預測的可能性得分為0.8的人,稱其為「喜歡」,而其他所有人則為「不喜歡」。

將腳本開發為可以在腳本啟動後的兩個小時內自動播放。

from likeliness_classifier import Classifier  import person_detector  import tensorflow as tf  from time import time    if __name__ == "__main__":      token = "YOUR-API-TOKEN"      api = tinderAPI(token)        detection_graph = person_detector.open_graph()      with detection_graph.as_default():          with tf.Session() as sess:                classifier = Classifier(graph="./tf/training_output/retrained_graph.pb",                                      labels="./tf/training_output/retrained_labels.txt")                end_time = time() + 60*60*2              while time() < end_time:                  try:                      persons = api.nearby_persons()                      pos_schools = ["Universität Zürich", "University of Zurich", "UZH"]                        for person in persons:                          score = person.predict_likeliness(classifier, sess)                            for school in pos_schools:                              if school in person.schools:                                  print()                                  score *= 1.2                            print("-------------------------")                          print("ID: ", person.id)                          print("Name: ", person.name)                          print("Schools: ", person.schools)                          print("Images: ", person.images)                          print(score)                            if score > 0.8:                              res = person.like()                              print("LIKE")                          else:                              res = person.dislike()                              print("DISLIKE")                  except Exception:                      pass    classifier.close()

現在可以讓腳本運行任意長的時間,並在不拖累拇指的情況下玩弄Tinder!

如果有任何疑問或發現錯誤,請隨時為Github存儲庫做出貢獻。

https://github.com/joelbarmettlerUZH/auto-tinder