{"cells":[{"metadata":{},"cell_type":"markdown","source":"# GPU Inference Kernel Demo\n\nIt works exactly the same as [Human Analog's inference demo](https://www.kaggle.com/humananalog/inference-demo), but works fine on the GPU.\n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"video_batch_size = 10\nframes_per_video = 17\ninput_size = 224\ntest_dir = \"/kaggle/input/deepfake-detection-challenge/test_videos/\"","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os, sys, time\nimport cv2\nimport numpy as np\nimport pandas as pd\n\nimport torch\nimport torch.nn as nn\nimport torch.nn.functional as F\n\n%matplotlib inline\nimport matplotlib.pyplot as plt","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Get the test videos"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"test_videos = sorted([x for x in os.listdir(test_dir) if x[-4:] == \".mp4\"])\nlen(test_videos)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Create helpers"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(\"PyTorch version:\", torch.__version__)\nprint(\"CUDA version:\", torch.version.cuda)\nprint(\"cuDNN version:\", torch.backends.cudnn.version())","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gpu = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\ngpu","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import sys\nsys.path.insert(0, \"/kaggle/input/blazeface-pytorch\")\nsys.path.insert(0, \"/kaggle/input/deepfakes-inference-demo\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from blazeface import BlazeFace\nfacedet = BlazeFace().to(gpu)\nfacedet.load_weights(\"/kaggle/input/blazeface-pytorch/blazeface.pth\")\nfacedet.load_anchors(\"/kaggle/input/blazeface-pytorch/anchors.npy\")\n_ = facedet.train(False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from helpers.read_video_1 import VideoReader\nfrom helpers.face_extract_1 import FaceExtractor\n\nvideo_reader = VideoReader()\nvideo_read_fn = lambda x: video_reader.read_frames(x, num_frames=frames_per_video)\nface_extractor = FaceExtractor(video_read_fn, facedet)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torch.nn as nn\nimport torchvision.models as models\n\nclass MyResNeXt(models.resnet.ResNet):\n    def __init__(self, training=True):\n        super(MyResNeXt, self).__init__(block=models.resnet.Bottleneck,\n                                        layers=[3, 4, 6, 3], \n                                        groups=32, \n                                        width_per_group=4)\n        self.fc = nn.Linear(2048, 1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"checkpoint = torch.load(\"/kaggle/input/deepfakes-inference-demo/resnext.pth\", map_location=gpu)\n\nmodel = MyResNeXt().to(gpu)\nmodel.load_state_dict(checkpoint)\n_ = model.eval()\n\ndel checkpoint","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Prediction loop"},{"metadata":{"trusted":true},"cell_type":"code","source":"def isotropically_resize_image(img, size, resample=cv2.INTER_AREA):\n    h, w = img.shape[:2]\n    if w > h:\n        h = h * size // w\n        w = size\n    else:\n        w = w * size // h\n        h = size\n\n    resized = cv2.resize(img, (w, h), interpolation=resample)\n    return resized\n\n\ndef make_square_image(img):\n    h, w = img.shape[:2]\n    size = max(h, w)\n    t = 0\n    b = size - h\n    l = 0\n    r = size - w\n    return cv2.copyMakeBorder(img, t, b, l, r, cv2.BORDER_CONSTANT, value=0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import torchvision.transforms as transforms\n\ntransform = transforms.Compose([\n    lambda x: isotropically_resize_image(x, input_size),\n    make_square_image,\n    transforms.ToPILImage(),\n    transforms.ToTensor(),\n    transforms.Normalize(\n        [0.485, 0.456, 0.406],\n        [0.229, 0.224, 0.225]\n    ),\n])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import collections, itertools\n\ndef predict_on_video(video_paths):\n    # Find the faces for N frames in the video.\n    # Only look at one face per frame.\n    faces = face_extractor.process_videos(test_dir, video_paths, range(len(video_paths)))\n    face_extractor.keep_only_best_face(faces)\n\n    videos = collections.defaultdict(list)\n    for face in faces:\n        if len(face['faces']) > 0:\n            videos[video_paths[face['video_idx']]].append(transform(face['faces'][0]))\n\n    data = [\n        (video_path, \n         torch.stack(videos[video_path]), \n         len(videos[video_path])\n        ) for video_path in video_paths if len(videos[video_path]) > 0\n    ]\n    \n    unknown_video_path = [video_path for video_path in video_paths if len(videos[video_path]) == 0]\n\n    if len(data) > 0:\n        known_video_path, video_tensors, video_lengths = zip(*data)\n        video_batch = torch.cat(video_tensors).to(gpu)\n\n        with torch.no_grad():\n            y_pred = model(video_batch)\n            y_pred = torch.sigmoid(y_pred.squeeze())\n        video_pred = [it.mean().item() for it in y_pred.split(video_lengths)]\n    else:\n        known_video_path, video_pred = tuple(), tuple()\n\n    known_answer = zip(known_video_path, video_pred)\n    unknown_answer = zip(unknown_video_path, [0.5] * len(unknown_video_path))\n\n    return itertools.chain(known_answer, unknown_answer)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def chunks(lst, n):\n    for i in range(0, len(lst), n):\n        yield lst[i:i + n]    \n\ndef predict_on_video_set(video_paths):\n    predictions = [predict_on_video(batch) for batch in chunks(video_paths, video_batch_size)]\n    return itertools.chain(*predictions)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"## Speed test"},{"metadata":{"trusted":true},"cell_type":"code","source":"speed_test = False  # you have to enable this manually\nif speed_test:\n    start_time = time.time()\n    predictions = predict_on_video_set(test_videos[:16])\n    print(time.time() - start_time)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"markdown","source":"## Make the submission"},{"metadata":{"trusted":true},"cell_type":"code","source":"results = predict_on_video_set(test_videos)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"filenames, labels = zip(*results)\nsubmission_df = pd.DataFrame({\"filename\": filenames, \"label\": labels})\nsubmission_df.to_csv(\"submission.csv\", index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%matplotlib inline\nsubmission_df.plot.hist(bins=11)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}