{"cells":[{"metadata":{},"cell_type":"markdown","source":"\n# Memorizer CGAN for dummies\n\n<img src=\"https://d33wubrfki0l68.cloudfront.net/b573d1c09e706a44b8f8d02ef010a57c1d502b9b/f5a93/assets/images/gan-intro/learning_10k_optim.gif\" height=\"200\" width=\"200\">\n\n\n**You're going to see:**\n\n1. GANs and CGANs\n2. How to destroy the FID metric.\n    - Best FID\n    - MiFID Penalization\n    \n    \n3. How Supervised GANs work and how to improve it.\n    - Discriminator memory test.\n    - Tricks to avoid MiFID Penalization.\n\n*From a noob point of view ;)*\n\n> This kernel is obviously based on: [Dog Memorizer GAN](https://www.kaggle.com/cdeotte/dog-memorizer-gan) , is pure knowledge so please upvote it, and the post [Memorization GAN Explained](https://www.kaggle.com/c/generative-dog-images/discussion/99215)\n\n> As you can check here: [Rules clarification on Generative Models](https://www.kaggle.com/c/generative-dog-images/discussion/98183#latest-573181) this method **is allowed.**\n\nIf you are interested in these kind of explanations, I can publish a kernel about my **Private LB simulation (Private Dataset and Private NN).**\n\n**<p>So, if this helps you, don't forget to <span style=\"color:red\">upvote</span>   :)</p>**\n\n<br>\n"},{"metadata":{},"cell_type":"markdown","source":"<br>\n**Libraries**"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport random\nfrom tqdm import tqdm\nimport shutil","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**LB calculation functions**\nfrom [Demo MiFID metric for Dog image generation comp](https://www.kaggle.com/wendykan/demo-mifid-metric-for-dog-image-generation-comp)"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"from __future__ import absolute_import, division, print_function\nimport numpy as np\nimport os\nimport gzip, pickle\nimport tensorflow as tf\nfrom scipy import linalg\nimport pathlib\nimport urllib\nimport warnings\nfrom tqdm import tqdm\nfrom PIL import Image\n\nclass KernelEvalException(Exception):\n    pass\n\nmodel_params = {\n    'Inception': {\n        'name': 'Inception', \n        'imsize': 64,\n        'output_layer': 'Pretrained_Net/pool_3:0', \n        'input_layer': 'Pretrained_Net/ExpandDims:0',\n        'output_shape': 2048,\n        'cosine_distance_eps': 0.1\n        }\n}\n\ndef create_model_graph(pth):\n    \"\"\"Creates a graph from saved GraphDef file.\"\"\"\n    # Creates graph from saved graph_def.pb.\n    with tf.gfile.FastGFile( pth, 'rb') as f:\n        graph_def = tf.GraphDef()\n        graph_def.ParseFromString( f.read())\n        _ = tf.import_graph_def( graph_def, name='Pretrained_Net')\n\ndef _get_model_layer(sess, model_name):\n    # layername = 'Pretrained_Net/final_layer/Mean:0'\n    layername = model_params[model_name]['output_layer']\n    layer = sess.graph.get_tensor_by_name(layername)\n    ops = layer.graph.get_operations()\n    for op_idx, op in enumerate(ops):\n        for o in op.outputs:\n            shape = o.get_shape()\n            if shape._dims != []:\n              shape = [s.value for s in shape]\n              new_shape = []\n              for j, s in enumerate(shape):\n                if s == 1 and j == 0:\n                  new_shape.append(None)\n                else:\n                  new_shape.append(s)\n              o.__dict__['_shape_val'] = tf.TensorShape(new_shape)\n    return layer\n\ndef get_activations(images, sess, model_name, batch_size=50, verbose=False):\n    \"\"\"Calculates the activations of the pool_3 layer for all images.\n\n    Params:\n    -- images      : Numpy array of dimension (n_images, hi, wi, 3). The values\n                     must lie between 0 and 256.\n    -- sess        : current session\n    -- batch_size  : the images numpy array is split into batches with batch size\n                     batch_size. A reasonable batch size depends on the disposable hardware.\n    -- verbose    : If set to True and parameter out_step is given, the number of calculated\n                     batches is reported.\n    Returns:\n    -- A numpy array of dimension (num images, 2048) that contains the\n       activations of the given tensor when feeding inception with the query tensor.\n    \"\"\"\n    inception_layer = _get_model_layer(sess, model_name)\n    n_images = images.shape[0]\n    if batch_size > n_images:\n        print(\"warning: batch size is bigger than the data size. setting batch size to data size\")\n        batch_size = n_images\n    n_batches = n_images//batch_size + 1\n    pred_arr = np.empty((n_images,model_params[model_name]['output_shape']))\n    for i in tqdm(range(n_batches)):\n        if verbose:\n            print(\"\\rPropagating batch %d/%d\" % (i+1, n_batches), end=\"\", flush=True)\n        start = i*batch_size\n        if start+batch_size < n_images:\n            end = start+batch_size\n        else:\n            end = n_images\n                    \n        batch = images[start:end]\n        pred = sess.run(inception_layer, {model_params[model_name]['input_layer']: batch})\n        pred_arr[start:end] = pred.reshape(-1,model_params[model_name]['output_shape'])\n    if verbose:\n        print(\" done\")\n    return pred_arr\n\n\n# def calculate_memorization_distance(features1, features2):\n#     neigh = NearestNeighbors(n_neighbors=1, algorithm='kd_tree', metric='euclidean')\n#     neigh.fit(features2) \n#     d, _ = neigh.kneighbors(features1, return_distance=True)\n#     print('d.shape=',d.shape)\n#     return np.mean(d)\n\ndef normalize_rows(x: np.ndarray):\n    \"\"\"\n    function that normalizes each row of the matrix x to have unit length.\n\n    Args:\n     ``x``: A numpy matrix of shape (n, m)\n\n    Returns:\n     ``x``: The normalized (by row) numpy matrix.\n    \"\"\"\n    return np.nan_to_num(x/np.linalg.norm(x, ord=2, axis=1, keepdims=True))\n\n\ndef cosine_distance(features1, features2):\n    # print('rows of zeros in features1 = ',sum(np.sum(features1, axis=1) == 0))\n    # print('rows of zeros in features2 = ',sum(np.sum(features2, axis=1) == 0))\n    features1_nozero = features1[np.sum(features1, axis=1) != 0]\n    features2_nozero = features2[np.sum(features2, axis=1) != 0]\n    norm_f1 = normalize_rows(features1_nozero)\n    norm_f2 = normalize_rows(features2_nozero)\n\n    d = 1.0-np.abs(np.matmul(norm_f1, norm_f2.T))\n    print('d.shape=',d.shape)\n    print('np.min(d, axis=1).shape=',np.min(d, axis=1).shape)\n    mean_min_d = np.mean(np.min(d, axis=1))\n    print('distance=',mean_min_d)\n    return mean_min_d\n\n\ndef distance_thresholding(d, eps):\n    if d < eps:\n        return d\n    else:\n        return 1\n\ndef calculate_frechet_distance(mu1, sigma1, mu2, sigma2, eps=1e-6):\n    \"\"\"Numpy implementation of the Frechet Distance.\n    The Frechet distance between two multivariate Gaussians X_1 ~ N(mu_1, C_1)\n    and X_2 ~ N(mu_2, C_2) is\n            d^2 = ||mu_1 - mu_2||^2 + Tr(C_1 + C_2 - 2*sqrt(C_1*C_2)).\n            \n    Stable version by Dougal J. Sutherland.\n\n    Params:\n    -- mu1 : Numpy array containing the activations of the pool_3 layer of the\n             inception net ( like returned by the function 'get_predictions')\n             for generated samples.\n    -- mu2   : The sample mean over activations of the pool_3 layer, precalcualted\n               on an representive data set.\n    -- sigma1: The covariance matrix over activations of the pool_3 layer for\n               generated samples.\n    -- sigma2: The covariance matrix over activations of the pool_3 layer,\n               precalcualted on an representive data set.\n\n    Returns:\n    --   : The Frechet Distance.\n    \"\"\"\n\n    mu1 = np.atleast_1d(mu1)\n    mu2 = np.atleast_1d(mu2)\n\n    sigma1 = np.atleast_2d(sigma1)\n    sigma2 = np.atleast_2d(sigma2)\n\n    assert mu1.shape == mu2.shape, \"Training and test mean vectors have different lengths\"\n    assert sigma1.shape == sigma2.shape, \"Training and test covariances have different dimensions\"\n\n    diff = mu1 - mu2\n\n    # product might be almost singular\n    covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False)\n    if not np.isfinite(covmean).all():\n        msg = \"fid calculation produces singular product; adding %s to diagonal of cov estimates\" % eps\n        warnings.warn(msg)\n        offset = np.eye(sigma1.shape[0]) * eps\n        # covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))\n        covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))\n    \n    # numerical error might give slight imaginary component\n    if np.iscomplexobj(covmean):\n        if not np.allclose(np.diagonal(covmean).imag, 0, atol=1e-3):\n            m = np.max(np.abs(covmean.imag))\n            raise ValueError(\"Imaginary component {}\".format(m))\n        covmean = covmean.real\n\n    # covmean = tf.linalg.sqrtm(tf.linalg.matmul(sigma1,sigma2))\n\n    print('covmean.shape=',covmean.shape)\n    # tr_covmean = tf.linalg.trace(covmean)\n\n    tr_covmean = np.trace(covmean)\n    return diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * tr_covmean\n    # return diff.dot(diff) + tf.linalg.trace(sigma1) + tf.linalg.trace(sigma2) - 2 * tr_covmean\n#-------------------------------------------------------------------------------\n\n\ndef calculate_activation_statistics(images, sess, model_name, batch_size=50, verbose=False):\n    \"\"\"Calculation of the statistics used by the FID.\n    Params:\n    -- images      : Numpy array of dimension (n_images, hi, wi, 3). The values\n                     must lie between 0 and 255.\n    -- sess        : current session\n    -- batch_size  : the images numpy array is split into batches with batch size\n                     batch_size. A reasonable batch size depends on the available hardware.\n    -- verbose     : If set to True and parameter out_step is given, the number of calculated\n                     batches is reported.\n    Returns:\n    -- mu    : The mean over samples of the activations of the pool_3 layer of\n               the incption model.\n    -- sigma : The covariance matrix of the activations of the pool_3 layer of\n               the incption model.\n    \"\"\"\n    act = get_activations(images, sess, model_name, batch_size, verbose)\n    mu = np.mean(act, axis=0)\n    sigma = np.cov(act, rowvar=False)\n    return mu, sigma, act\n    \ndef _handle_path_memorization(path, sess, model_name, is_checksize, is_check_png):\n    path = pathlib.Path(path)\n    files = list(path.glob('*.jpg')) + list(path.glob('*.png'))\n    imsize = model_params[model_name]['imsize']\n\n    # In production we don't resize input images. This is just for demo purpose. \n    x = np.array([np.array(img_read_checks(fn, imsize, is_checksize, imsize, is_check_png)) for fn in files])\n    m, s, features = calculate_activation_statistics(x, sess, model_name)\n    del x #clean up memory\n    return m, s, features\n\n# check for image size\ndef img_read_checks(filename, resize_to, is_checksize=False, check_imsize = 64, is_check_png = False):\n    im = Image.open(str(filename))\n    if is_checksize and im.size != (check_imsize,check_imsize):\n        raise KernelEvalException('The images are not of size '+str(check_imsize))\n    \n    if is_check_png and im.format != 'PNG':\n        raise KernelEvalException('Only PNG images should be submitted.')\n\n    if resize_to is None:\n        return im\n    else:\n        return im.resize((resize_to,resize_to),Image.ANTIALIAS)\n\ndef calculate_kid_given_paths(paths, model_name, model_path, feature_path=None):\n    ''' Calculates the KID of two paths. '''\n    tf.reset_default_graph()\n    create_model_graph(str(model_path))\n    with tf.Session() as sess:\n        sess.run(tf.global_variables_initializer())\n        m1, s1, features1 = _handle_path_memorization(paths[0], sess, model_name, is_checksize = True, is_check_png = True)\n        if feature_path is None:\n            m2, s2, features2 = _handle_path_memorization(paths[1], sess, model_name, is_checksize = False, is_check_png = False)\n        else:\n            with np.load(feature_path) as f:\n                m2, s2, features2 = f['m'], f['s'], f['features']\n\n        print('m1,m2 shape=',(m1.shape,m2.shape),'s1,s2=',(s1.shape,s2.shape))\n        print('starting calculating FID')\n        fid_value = calculate_frechet_distance(m1, s1, m2, s2)\n        print('done with FID, starting distance calculation')\n        distance = cosine_distance(features1, features2)        \n        return fid_value, distance","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# GANs and CGANs\n\n\n### GAN\n\n**Check my baseline [GAN Introduction](https://www.kaggle.com/jesucristo/gan-introduction)**\n\n\n<img src=\"https://4.bp.blogspot.com/-YTGLQjhch-Q/Wz475NU2TSI/AAAAAAAAsu4/zaC_wfZBX80dePLflgQUaAaxE72od3VCgCEwYBhgL/s1600/Figura_2.png\" height=\"500\" width=\"500\">\n\n\n### [CGAN](https://arxiv.org/abs/1411.1784) (Conditional GAN)\n\n![](https://miro.medium.com/max/960/1*-buiUh_PpX_XkNOzwt83Cw.png)\n\n> When you train your Generator and Discriminator, give it a breed of dog (y). Then after your GAN is trained, you feed a random Z plus a random Breed ( y = 1 to 120) into your Generator.\n\n**Example**\n\nLike Kostya Atarik did [here](https://www.kaggle.com/c/generative-dog-images/discussion/97753#latest-572996) training only on one breed of *Maltese Dog*\n\n![](https://www.googleapis.com/download/storage/v1/b/kaggle-user-content/o/inbox%2F1544891%2Fd61e20e33a27917e3ee8b817df3037d1%2Ftrain_00014450.png?generation=1562682396674729&alt=media)\n\n\nFrom Chris original kernel\n\n> A GAN consists of a Generator and Discriminator. After being trained, a Generator is a robot artist that draws dog images. During training the Discriminator teaches the Generator how to draw a dog. (And the G teaches the D also). The Generator never sees any images of dogs. Instead it continually attempts to draw a dog and is coached by the Discriminator. In this kernel, the **Memorizer** Generator is only coached to memorize images from the training set. In contrast, a **Generalizing** Generator is coached to draw images that have never existed nor been seen before!"},{"metadata":{},"cell_type":"markdown","source":"# How to destroy the FID metric.\n\nIn my post [Explaining the metric FID](https://www.kaggle.com/c/generative-dog-images/discussion/97809#latest-566998) , I explain IS and FID metrics and how they work.\n\nThe better the quality and variety, the lower the score = better LB.\n**Then what would happen if we sent 10k real images?**"},{"metadata":{},"cell_type":"markdown","source":"- I take a sample of 10k dogs from the original images.\n- move them to images folder\n- Resize and submit"},{"metadata":{"trusted":true},"cell_type":"code","source":"!ls ../input/","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"dogs = os.listdir('../input/generative-dog-images/all-dogs/all-dogs/')\nprint (\"Total images = \", len(dogs))\nsub = random.sample(dogs, 10000)\nprint (\"Sample images = \", len(sub))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"!pwd\n!mkdir images\n!ls","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"for i in tqdm(sub):\n    shutil.copy(\"../input/generative-dog-images/all-dogs/all-dogs/\"+i, \"/kaggle/working/images/\"+i)","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"from PIL import Image\nimport os, sys\n\npath = '/kaggle/working/images/'\n\ndef resize():\n    for item in tqdm(os.listdir(path)):\n        im = Image.open(path+item)\n        imResize = im.resize((64,64), Image.ANTIALIAS)\n        imResize.save(path+item, 'PNG', quality=100)\n\nresize()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**LB score using 10k real images**"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"user_images_unzipped_path = 'images'\nimages_path = [user_images_unzipped_path,'../input/generative-dog-images/all-dogs/all-dogs/']\n\npublic_path = '../input/dog-face-generation-competition-kid-metric-input/classify_image_graph_def.pb'\n\nfid_epsilon = 10e-15\n\nfid_value_public, distance_public = calculate_kid_given_paths(images_path, 'Inception', public_path)\ndistance_public = distance_thresholding(distance_public, model_params['Inception']['cosine_distance_eps'])\nprint(\"FID_public: \", fid_value_public, \"distance_public: \", distance_public, \"multiplied_public: \", fid_value_public /(distance_public + fid_epsilon))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"!rm -r images","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### The best FID we can get is around 1.14 !\n\nChris score right now is **5.57713** that means:\n1. his images are very similar to the real ones\n2. he can keep improving ;)\n\nObviously kaggle realized about this and that's why they use the metric **MiFID (Memorization-informed FID).** \nHowever, like Chris said at [Supervised Generative Dog Net](https://www.kaggle.com/cdeotte/supervised-generative-dog-net)\n\n> The problem with generative methods that memorize training images is that it allows the submission of essentially original images. For example submit 99% original image 1 with 1% original image 2 added. Then essentially we would be submitting image 1. Furthermore the MiFID metric doesn't recognize that cropped images are the same as original images. Therefore a memorizing generative method using cropped images can score very good LB.\n\n### we can fool MiFID metric too! nothing new, now you're going to learn how\n\nNow you understand my post [LB 428408000297.81800](https://www.kaggle.com/c/generative-dog-images/discussion/98012#latest-567024) yeah, I know, sometimes I'm a troll ;)\n\nAnd like Anton Chikin said:\n> I think your submission is just a subset of training images. In this case you've received a huge penalty by memorization part of MiFID\n\n"},{"metadata":{},"cell_type":"markdown","source":"<br>\n# How Supervised GANs work and how to improve it.\n\n> remember, thsi code is from: [Dog Memorizer GAN](https://www.kaggle.com/cdeotte/dog-memorizer-gan)"},{"metadata":{},"cell_type":"markdown","source":"### Load and Crop images"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"ComputeLB = True\nDogsOnly = False\n\nimport numpy as np, pandas as pd, os\nimport xml.etree.ElementTree as ET \nimport matplotlib.pyplot as plt, zipfile \nfrom PIL import Image \n\nROOT = '../input/generative-dog-images/'\nif not ComputeLB: ROOT = '../input/'\nIMAGES = os.listdir(ROOT + 'all-dogs/all-dogs/')\nbreeds = os.listdir(ROOT + 'annotation/Annotation/') \n\nidxIn = 0; namesIn = []\nimagesIn = np.zeros((25000,64,64,3))\n\n# CROP WITH BOUNDING BOXES TO GET DOGS ONLY\n# https://www.kaggle.com/paulorzp/show-annotations-and-breeds\nif DogsOnly:\n    for breed in breeds:\n        for dog in os.listdir(ROOT+'annotation/Annotation/'+breed):\n            try: img = Image.open(ROOT+'all-dogs/all-dogs/'+dog+'.jpg') \n            except: continue           \n            tree = ET.parse(ROOT+'annotation/Annotation/'+breed+'/'+dog)\n            root = tree.getroot()\n            objects = root.findall('object')\n            for o in objects:\n                bndbox = o.find('bndbox') \n                xmin = int(bndbox.find('xmin').text)\n                ymin = int(bndbox.find('ymin').text)\n                xmax = int(bndbox.find('xmax').text)\n                ymax = int(bndbox.find('ymax').text)\n                w = np.min((xmax - xmin, ymax - ymin))\n                img2 = img.crop((xmin, ymin, xmin+w, ymin+w))\n                img2 = img2.resize((64,64), Image.ANTIALIAS)\n                imagesIn[idxIn,:,:,:] = np.asarray(img2)\n                #if idxIn%1000==0: print(idxIn)\n                namesIn.append(breed)\n                idxIn += 1\n    idx = np.arange(idxIn)\n    np.random.shuffle(idx)\n    imagesIn = imagesIn[idx,:,:,:]\n    namesIn = np.array(namesIn)[idx]\n    \n# RANDOMLY CROP FULL IMAGES\nelse:\n    IMAGES = np.sort(IMAGES)\n    np.random.seed(810)\n    x = np.random.choice(np.arange(20579),10000)\n    np.random.seed(None)\n    for k in range(len(x)):\n        img = Image.open(ROOT + 'all-dogs/all-dogs/' + IMAGES[x[k]])\n        w = img.size[0]; h = img.size[1];\n        if (k%2==0)|(k%3==0):\n            w2 = 100; h2 = int(h/(w/100))\n            a = 18; b = 0          \n        else:\n            a=0; b=0\n            if w<h:\n                w2 = 64; h2 = int((64/w)*h)\n                b = (h2-64)//2\n            else:\n                h2 = 64; w2 = int((64/h)*w)\n                a = (w2-64)//2\n        img = img.resize((w2,h2), Image.ANTIALIAS)\n        img = img.crop((0+a, 0+b, 64+a, 64+b))    \n        imagesIn[idxIn,:,:,:] = np.asarray(img)\n        namesIn.append(IMAGES[x[k]])\n        #if idxIn%1000==0: print(idxIn)\n        idxIn += 1\n    \n# DISPLAY CROPPED IMAGES\nx = np.random.randint(0,idxIn,25)\nfor k in range(5):\n    plt.figure(figsize=(15,3))\n    for j in range(5):\n        plt.subplot(1,5,j+1)\n        img = Image.fromarray( imagesIn[x[k*5+j],:,:,:].astype('uint8') )\n        plt.axis('off')\n        if not DogsOnly: plt.title(namesIn[x[k*5+j]],fontsize=11)\n        else: plt.title(namesIn[x[k*5+j]].split('-')[1],fontsize=11)\n        plt.imshow(img)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","collapsed":true,"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":false},"cell_type":"markdown","source":"## Discriminator"},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"from keras.models import Model\nfrom keras.layers import Input, Dense, Conv2D, Reshape, Flatten, concatenate\nfrom keras.preprocessing.image import ImageDataGenerator\nfrom keras.callbacks import LearningRateScheduler\nfrom keras.optimizers import SGD, Adam","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"A **simple GAN discriminator** is like: (click input to see)"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def define_discriminator(in_shape=(28,28,1)):\n\tmodel = Sequential()\n\t# downsample\n\tmodel.add(Conv2D(128, (3,3), strides=(2,2), padding='same', input_shape=in_shape))\n\tmodel.add(LeakyReLU(alpha=0.2))\n\t# downsample\n\tmodel.add(Conv2D(128, (3,3), strides=(2,2), padding='same'))\n\tmodel.add(LeakyReLU(alpha=0.2))\n\t# classifier\n\tmodel.add(Flatten())\n\tmodel.add(Dropout(0.4))\n\tmodel.add(Dense(1, activation='sigmoid'))\n\t# compile model\n\topt = Adam(lr=0.0002, beta_1=0.5)\n\tmodel.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])\n\treturn model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"A **simple Conditional GAN (CGAN) discriminator** is like: (click input to see)"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"# define the standalone discriminator model\ndef define_discriminator(in_shape=(28,28,1), n_classes=10):\n\t# label input\n\tin_label = Input(shape=(1,))\n\t# embedding for categorical input\n\tli = Embedding(n_classes, 50)(in_label)\n\t# scale up to image dimensions with linear activation\n\tn_nodes = in_shape[0] * in_shape[1]\n\tli = Dense(n_nodes)(li)\n\t# reshape to additional channel\n\tli = Reshape((in_shape[0], in_shape[1], 1))(li)\n\t# image input\n\tin_image = Input(shape=in_shape)\n\t# concat label as a channel\n\tmerge = Concatenate()([in_image, li])\n\t# downsample\n\tfe = Conv2D(128, (3,3), strides=(2,2), padding='same')(merge)\n\tfe = LeakyReLU(alpha=0.2)(fe)\n\t# downsample\n\tfe = Conv2D(128, (3,3), strides=(2,2), padding='same')(fe)\n\tfe = LeakyReLU(alpha=0.2)(fe)\n\t# flatten feature maps\n\tfe = Flatten()(fe)\n\t# dropout\n\tfe = Dropout(0.4)(fe)\n\t# output\n\tout_layer = Dense(1, activation='sigmoid')(fe)\n\t# define model\n\tmodel = Model([in_image, in_label], out_layer)\n\t# compile model\n\topt = Adam(lr=0.0002, beta_1=0.5)\n\tmodel.compile(loss='binary_crossentropy', optimizer=opt, metrics=['accuracy'])\n\treturn model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"If we check **Supervised GAN Discriminator is a very simple version of CGAN.**\n> The model takes ```[dog,dogName]``` (dogName = label y)"},{"metadata":{"trusted":true},"cell_type":"code","source":"# BUILD DISCRIMINATIVE NETWORK\ndog = Input((12288,))\ndogName = Input((10000,))\nx = Dense(12288, activation='sigmoid')(dogName) \nx = Reshape((2,12288,1))(concatenate([dog,x]))\nx = Conv2D(1,(2,1),use_bias=False,name='conv')(x)\ndiscriminated = Flatten()(x)\n\n# COMPILE\ndiscriminator = Model([dog,dogName], discriminated)\ndiscriminator.get_layer('conv').trainable = False\ndiscriminator.get_layer('conv').set_weights([np.array([[[[-1.0 ]]],[[[1.0]]]])])\ndiscriminator.compile(optimizer='adam', loss='binary_crossentropy')\n\n# DISPLAY ARCHITECTURE\n#discriminator.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Train Discriminator\nWe will train the Discriminator to memorize the training images. (Typically you don't train the Discriminator ahead of time. The D learns as the G learns. But this GAN is special)."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# TRAINING DATA\ntrain_y = (imagesIn[:10000,:,:,:]/255.).reshape((-1,12288))\ntrain_X = np.zeros((10000,10000))\nfor i in range(10000): train_X[i,i] = 1\nzeros = np.zeros((10000,12288))\n\n# TRAIN NETWORK\nlr = 0.5\nfor k in range(5):\n    annealer = LearningRateScheduler(lambda x: lr)\n    h = discriminator.fit([zeros,train_X], train_y, epochs = 20, batch_size=256, callbacks=[annealer], verbose=0)\n    print('Epoch',(k+1)*10,'/50 - loss =',h.history['loss'][-1] )\n    if h.history['loss'][-1]<0.530: lr = 0.1","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Delete Training Images\n\n**Our Discriminator has memorized all the training images.** We will now delete the training images. Our Generator will never see the training images. It will only be coached by the Discriminator. Below are examples of images that the Discriminator memorized."},{"metadata":{"trusted":true},"cell_type":"code","source":"#del train_X, train_y, imagesIn","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Important question\n\n> **me: Are Memorizer GANs limited by the number of images the discriminator can memorize? in that case the number of different dogs that can be generated is 10k ?**\n\n> **chris:** There is no limitation. A Memorizer GAN can memorize any number of images. But it is a finite number. If a Memorizer GAN is not improved upon, then if you ask for an image that it did not memorize, it will look poor.\nFor example, say it memorizes 3 images and has an input seed of length 3. Then seed = [1, 0, 0] recalls the first image, and seeds [0, 1, 0] and [0, 0, 1] recall the other two. However, there are an infinite number of seeds. What if we input seed = [0.5, 0.9, -0.3]? Then the outputted image will look bad.\nA Generalizer GAN will output nice looking image for every seed. If you input seed=[1, 0, 0] it will look nice. If you input seed = [0.5, 0.9, -0.3] it will look nice. A Generalizer GAN can draw an infinite number of images that look good !"},{"metadata":{},"cell_type":"markdown","source":"Let's generate 9 memorized dogs:\n> NOTE: \n```\nxx = np.zeros((10000))\nxx[np.random.randint(10000)] = 1 # one dog from memory\n```"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print('Discriminator Recalls from Memory Dogs')    \nfor k in range(3):\n    plt.figure(figsize=(10,3))\n    for j in range(3):\n        xx = np.zeros((10000))\n        xx[np.random.randint(10000)] = 1 # one dog from memory\n        plt.subplot(1,3,j+1)\n        img = discriminator.predict([zeros[0,:].reshape((-1,12288)),xx.reshape((-1,10000))]).reshape((-1,64,64,3))\n        img = Image.fromarray( (255*img).astype('uint8').reshape((64,64,3)))\n        plt.axis('off')\n        plt.imshow(img)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Like real images!**\n> Epoch 50 /50 - loss = 0.5325211682319642\n\nSpolier: the discriminator coaches the generator.\n\n**great discriminator = great generator = great LB ? NO ... not exactly**\n\nProbably many people tried to reduce ```discriminator loss``` and their score worsened.\nRemember what happened when we submitted 10k real images, the FID was great (1.4~) but the MiFID is really bad ( millions!). So why these real images don't score fatally?\n\n>  Furthermore the MiFID metric doesn't recognize that **cropped images** are the same as original images. Therefore a memorizing generative method using **cropped images** can score very good LB.\n\n<br>\n### Let's check the memory\n\n> chris added ```BadMemory``` option [Dog Memorizer GAN](https://www.kaggle.com/cdeotte/dog-memorizer-gan?scriptVersionId=16814734) V6"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print('Discriminator Recalls from Memory Dogs - 10% dog')    \nfor k in range(2):\n    plt.figure(figsize=(4,2))\n    for j in range(2):\n        xx = np.zeros((10000))\n        xx[np.random.randint(10000)] = 0.1 # one dog from memory\n        plt.subplot(1,2,j+1)\n        img = discriminator.predict([zeros[0,:].reshape((-1,12288)),xx.reshape((-1,10000))]).reshape((-1,64,64,3))\n        img = Image.fromarray( (255*img).astype('uint8').reshape((64,64,3)))\n        plt.axis('off')\n        plt.imshow(img)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print('Discriminator Memory Dogs - 30% dog')    \nfor k in range(2):\n    plt.figure(figsize=(4,2))\n    for j in range(2):\n        xx = np.zeros((10000))\n        xx[np.random.randint(10000)] = 0.3 # one dog from memory\n        plt.subplot(1,2,j+1)\n        img = discriminator.predict([zeros[0,:].reshape((-1,12288)),xx.reshape((-1,10000))]).reshape((-1,64,64,3))\n        img = Image.fromarray( (255*img).astype('uint8').reshape((64,64,3)))\n        plt.axis('off')\n        plt.imshow(img)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**2 dogs in one picture? **\n\n```\nxx = np.zeros((10000))\nxx[np.random.randint(10000)] = 0.5 # one dog from memory\nxx[np.random.randint(10000)] = 0.5 # one dog from memory\n```\n\nit's like we are **blending pixels!**"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print('Discriminator Recalls from Memory Dogs - 2 dogs')    \nfor k in range(2):\n    plt.figure(figsize=(8,3))\n    for j in range(2):\n        xx = np.zeros((10000))\n        xx[np.random.randint(10000)] = 0.5 # one dog from memory\n        xx[np.random.randint(10000)] = 0.5 # one dog from memory\n        plt.subplot(1,2,j+1)\n        img = discriminator.predict([zeros[0,:].reshape((-1,12288)),xx.reshape((-1,10000))]).reshape((-1,64,64,3))\n        img = Image.fromarray( (255*img).astype('uint8').reshape((64,64,3)))\n        plt.axis('off')\n        plt.imshow(img)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### The final trick\n\n- resize + crop + n dogs in the same picture!\n\n```\nxx[np.random.randint(10000)] = 0.999 # one dog from memory\nxx[np.random.randint(10000)] = 0.001 # one dog from memory\n```\n\nIn front of our eyes, they look like real pictures, but the **Inception model** doesn't see the same ;)\n**That's why you can generate \"real pictures\" and avoid MiFID penalization**\n"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"print('Discriminator Recalls from Memory Dogs - 2 dogs')    \nfor k in range(2):\n    plt.figure(figsize=(8,3))\n    for j in range(2):\n        xx = np.zeros((10000))\n        xx[np.random.randint(10000)] = 0.999 # one dog from memory\n        xx[np.random.randint(10000)] = 0.001 # one dog from memory\n        plt.subplot(1,2,j+1)\n        img = discriminator.predict([zeros[0,:].reshape((-1,12288)),xx.reshape((-1,10000))]).reshape((-1,64,64,3))\n        img = Image.fromarray( (255*img).astype('uint8').reshape((64,64,3)))\n        plt.axis('off')\n        plt.imshow(img)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<br>\n## Build Generator and GAN"},{"metadata":{},"cell_type":"markdown","source":"A **simple GAN generator** is like: (click input to see)"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def define_generator(latent_dim):\n\tmodel = Sequential()\n\t# foundation for 7x7 image\n\tn_nodes = 128 * 7 * 7\n\tmodel.add(Dense(n_nodes, input_dim=latent_dim))\n\tmodel.add(LeakyReLU(alpha=0.2))\n\tmodel.add(Reshape((7, 7, 128)))\n\t# upsample to 14x14\n\tmodel.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same'))\n\tmodel.add(LeakyReLU(alpha=0.2))\n\t# upsample to 28x28\n\tmodel.add(Conv2DTranspose(128, (4,4), strides=(2,2), padding='same'))\n\tmodel.add(LeakyReLU(alpha=0.2))\n\t# generate\n\tmodel.add(Conv2D(1, (7,7), activation='tanh', padding='same'))\n\treturn model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"A **simple Conditional GAN (CGAN) generator** is like: (click input to see)"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"def define_generator(latent_dim, n_classes=120):\n\t# label input\n\tin_label = Input(shape=(1,))\n\t# embedding for categorical input\n\tli = Embedding(n_classes, 50)(in_label)\n\t# linear multiplication\n\tn_nodes = 7 * 7\n\tli = Dense(n_nodes)(li)\n\t# reshape to additional channel\n\tli = Reshape((7, 7, 1))(li)\n\t# image generator input\n\tin_lat = Input(shape=(latent_dim,))\n\t# foundation for 7x7 image\n\tn_nodes = 128 * 7 * 7\n\tgen = Dense(n_nodes)(in_lat)\n\tgen = LeakyReLU(alpha=0.2)(gen)\n\tgen = Reshape((7, 7, 128))(gen)\n\t# merge image gen and label input\n\tmerge = Concatenate()([gen, li])\n\t# upsample to 14x14\n\tgen = Conv2DTranspose(128, (4,4), strides=(2,2), padding='same')(merge)\n\tgen = LeakyReLU(alpha=0.2)(gen)\n\t# upsample to 28x28\n\tgen = Conv2DTranspose(128, (4,4), strides=(2,2), padding='same')(gen)\n\tgen = LeakyReLU(alpha=0.2)(gen)\n\t# output\n\tout_layer = Conv2D(1, (7,7), activation='tanh', padding='same')(gen)\n\t# define model\n\tmodel = Model([in_lat, in_label], out_layer)\n\treturn model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"If we check **Supervised GAN Generator is a very simple version of CGAN. ¿?**"},{"metadata":{"trusted":true},"cell_type":"code","source":"# BUILD GENERATOR NETWORK\nseed = Input((10000,))\ngenerated = Dense(12288, activation='linear')(seed)\n\n# COMPILE\ngenerator = Model(seed, [generated,Reshape((10000,))(seed)])\n\n# DISPLAY ARCHITECTURE\ngenerator.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# BUILD GENERATIVE ADVERSARIAL NETWORK\ndiscriminator.trainable=False    \ngan_input = Input(shape=(10000,))\nx = generator(gan_input)\ngan_output = discriminator(x)\n\n# COMPILE GAN\ngan = Model(gan_input, gan_output)\ngan.get_layer('model_1').get_layer('conv').set_weights([np.array([[[[-1 ]]],[[[255.]]]])])\ngan.compile(optimizer=Adam(5), loss='mean_squared_error')\n\n# DISPLAY ARCHITECTURE\n#gan.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Discriminator Coaches Generator\nIn a typical GAN, the discriminator does not memorize the training images beforehand. Instead it learns to distinquish real images from fake images at the same time that the Generator learns to make fake images. In this GAN, we taught the Discriminator ahead of time and it will now teach the Generator."},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"# TRAINING DATA\ntrain = np.zeros((10000,10000))\nfor i in range(10000): train[i,i] = 1\nzeros = np.zeros((10000,12288))\n\n# TRAIN NETWORKS\nlr = 5.\nfor k in range(50):  \n\n    # BEGIN DISCRIMINATOR COACHES GENERATOR\n    annealer = LearningRateScheduler(lambda x: lr)\n    h = gan.fit(train, zeros, epochs = 1, batch_size=256, callbacks=[annealer], verbose=0)\n    if (k<10)|(k%5==4):\n        print('Epoch',(k+1)*10,'/500 - loss =',h.history['loss'][-1] )\n    if h.history['loss'][-1] < 25: lr = 1.\n    if h.history['loss'][-1] < 1.5: lr = 0.5\n        \n    # DISPLAY GENERATOR LEARNING PROGRESS\n    if k<10:        \n        plt.figure(figsize=(15,3))\n        for j in range(5):\n            xx = np.zeros((10000))\n            xx[np.random.randint(10000)] = 1\n            plt.subplot(1,5,j+1)\n            img = generator.predict(xx.reshape((-1,10000)))[0].reshape((-1,64,64,3))\n            img = Image.fromarray( (img).astype('uint8').reshape((64,64,3)))\n            plt.axis('off')\n            plt.imshow(img)\n        plt.show()  ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Build Generator Class\n\nOur Generative Network has now **learned all the training images from our Discriminative Network.** We would like our Dog Generator to accept any random 100 dimensional vector and output an image. Furthermore we need to slightly perturb each memorized image so we don't submit exact copies of training images. Let's build a Generator Class"},{"metadata":{},"cell_type":"markdown","source":"**Remember**\n\n```\nxx[self.index] = 0.999\nxx[np.random.randint(10000)] = 0.001\n```"},{"metadata":{"trusted":true},"cell_type":"code","source":"class DogGenerator:\n    index = 0   \n    def getDog(self,seed):\n        xx = np.zeros((10000))\n        xx[self.index] = 0.999\n        xx[np.random.randint(10000)] = 0.001\n        img = generator.predict(xx.reshape((-1,10000)))[0].reshape((64,64,3))\n        self.index = (self.index+1)%10000\n        return Image.fromarray( img.astype('uint8') ) ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"print('Generated dogs 99% real 1% fake')\nd = DogGenerator()\nfor k in range(2):\n    plt.figure(figsize=(8,3))\n    for j in range(2):\n        plt.subplot(1,2,j+1)\n        img = d.getDog(seed = np.random.normal(0,1,100))\n        plt.axis('off')\n        plt.imshow(img)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Submit to Kaggle\nMy hope is that Kaggle disallows **Memorizer** GANs from winning medals in this competition. The purpose of submitting this high scoring kernel is to demonstrate that it is too easy to achieve a high score with a simple **Memorizer** GAN. The more complex and interesting solutions are **Generalizing** GANs!"},{"metadata":{"trusted":true},"cell_type":"code","source":"# SAVE TO ZIP FILE NAMED IMAGES.ZIP\nz = zipfile.PyZipFile('images.zip', mode='w')\nd = DogGenerator()\nfor k in range(10000):\n    img = d.getDog(np.random.normal(0,1,100))\n    f = str(k)+'.png'\n    img.save(f,'PNG'); z.write(f); os.remove(f)\n    #if k % 1000==0: print(k)\nz.close()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Calculate LB Score\nIf you wish to compute LB, you must add the LB metric dataset [here][1] to this kernel and change the boolean variable in the first cell block.\n\n[1]: https://www.kaggle.com/wendykan/dog-face-generation-competition-kid-metric-input"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"from __future__ import absolute_import, division, print_function\nimport numpy as np\nimport os\nimport gzip, pickle\nimport tensorflow as tf\nfrom scipy import linalg\nimport pathlib\nimport urllib\nimport warnings\nfrom tqdm import tqdm\nfrom PIL import Image\n\nclass KernelEvalException(Exception):\n    pass\n\nmodel_params = {\n    'Inception': {\n        'name': 'Inception', \n        'imsize': 64,\n        'output_layer': 'Pretrained_Net/pool_3:0', \n        'input_layer': 'Pretrained_Net/ExpandDims:0',\n        'output_shape': 2048,\n        'cosine_distance_eps': 0.1\n        }\n}\n\ndef create_model_graph(pth):\n    \"\"\"Creates a graph from saved GraphDef file.\"\"\"\n    # Creates graph from saved graph_def.pb.\n    with tf.gfile.FastGFile( pth, 'rb') as f:\n        graph_def = tf.GraphDef()\n        graph_def.ParseFromString( f.read())\n        _ = tf.import_graph_def( graph_def, name='Pretrained_Net')\n\ndef _get_model_layer(sess, model_name):\n    # layername = 'Pretrained_Net/final_layer/Mean:0'\n    layername = model_params[model_name]['output_layer']\n    layer = sess.graph.get_tensor_by_name(layername)\n    ops = layer.graph.get_operations()\n    for op_idx, op in enumerate(ops):\n        for o in op.outputs:\n            shape = o.get_shape()\n            if shape._dims != []:\n              shape = [s.value for s in shape]\n              new_shape = []\n              for j, s in enumerate(shape):\n                if s == 1 and j == 0:\n                  new_shape.append(None)\n                else:\n                  new_shape.append(s)\n              o.__dict__['_shape_val'] = tf.TensorShape(new_shape)\n    return layer\n\ndef get_activations(images, sess, model_name, batch_size=50, verbose=False):\n    \"\"\"Calculates the activations of the pool_3 layer for all images.\n\n    Params:\n    -- images      : Numpy array of dimension (n_images, hi, wi, 3). The values\n                     must lie between 0 and 256.\n    -- sess        : current session\n    -- batch_size  : the images numpy array is split into batches with batch size\n                     batch_size. A reasonable batch size depends on the disposable hardware.\n    -- verbose    : If set to True and parameter out_step is given, the number of calculated\n                     batches is reported.\n    Returns:\n    -- A numpy array of dimension (num images, 2048) that contains the\n       activations of the given tensor when feeding inception with the query tensor.\n    \"\"\"\n    inception_layer = _get_model_layer(sess, model_name)\n    n_images = images.shape[0]\n    if batch_size > n_images:\n        print(\"warning: batch size is bigger than the data size. setting batch size to data size\")\n        batch_size = n_images\n    n_batches = n_images//batch_size + 1\n    pred_arr = np.empty((n_images,model_params[model_name]['output_shape']))\n    for i in tqdm(range(n_batches)):\n        if verbose:\n            print(\"\\rPropagating batch %d/%d\" % (i+1, n_batches), end=\"\", flush=True)\n        start = i*batch_size\n        if start+batch_size < n_images:\n            end = start+batch_size\n        else:\n            end = n_images\n                    \n        batch = images[start:end]\n        pred = sess.run(inception_layer, {model_params[model_name]['input_layer']: batch})\n        pred_arr[start:end] = pred.reshape(-1,model_params[model_name]['output_shape'])\n    if verbose:\n        print(\" done\")\n    return pred_arr\n\n\n# def calculate_memorization_distance(features1, features2):\n#     neigh = NearestNeighbors(n_neighbors=1, algorithm='kd_tree', metric='euclidean')\n#     neigh.fit(features2) \n#     d, _ = neigh.kneighbors(features1, return_distance=True)\n#     print('d.shape=',d.shape)\n#     return np.mean(d)\n\ndef normalize_rows(x: np.ndarray):\n    \"\"\"\n    function that normalizes each row of the matrix x to have unit length.\n\n    Args:\n     ``x``: A numpy matrix of shape (n, m)\n\n    Returns:\n     ``x``: The normalized (by row) numpy matrix.\n    \"\"\"\n    return np.nan_to_num(x/np.linalg.norm(x, ord=2, axis=1, keepdims=True))\n\n\ndef cosine_distance(features1, features2):\n    # print('rows of zeros in features1 = ',sum(np.sum(features1, axis=1) == 0))\n    # print('rows of zeros in features2 = ',sum(np.sum(features2, axis=1) == 0))\n    features1_nozero = features1[np.sum(features1, axis=1) != 0]\n    features2_nozero = features2[np.sum(features2, axis=1) != 0]\n    norm_f1 = normalize_rows(features1_nozero)\n    norm_f2 = normalize_rows(features2_nozero)\n\n    d = 1.0-np.abs(np.matmul(norm_f1, norm_f2.T))\n    print('d.shape=',d.shape)\n    print('np.min(d, axis=1).shape=',np.min(d, axis=1).shape)\n    mean_min_d = np.mean(np.min(d, axis=1))\n    print('distance=',mean_min_d)\n    return mean_min_d\n\n\ndef distance_thresholding(d, eps):\n    if d < eps:\n        return d\n    else:\n        return 1\n\ndef calculate_frechet_distance(mu1, sigma1, mu2, sigma2, eps=1e-6):\n    \"\"\"Numpy implementation of the Frechet Distance.\n    The Frechet distance between two multivariate Gaussians X_1 ~ N(mu_1, C_1)\n    and X_2 ~ N(mu_2, C_2) is\n            d^2 = ||mu_1 - mu_2||^2 + Tr(C_1 + C_2 - 2*sqrt(C_1*C_2)).\n            \n    Stable version by Dougal J. Sutherland.\n\n    Params:\n    -- mu1 : Numpy array containing the activations of the pool_3 layer of the\n             inception net ( like returned by the function 'get_predictions')\n             for generated samples.\n    -- mu2   : The sample mean over activations of the pool_3 layer, precalcualted\n               on an representive data set.\n    -- sigma1: The covariance matrix over activations of the pool_3 layer for\n               generated samples.\n    -- sigma2: The covariance matrix over activations of the pool_3 layer,\n               precalcualted on an representive data set.\n\n    Returns:\n    --   : The Frechet Distance.\n    \"\"\"\n\n    mu1 = np.atleast_1d(mu1)\n    mu2 = np.atleast_1d(mu2)\n\n    sigma1 = np.atleast_2d(sigma1)\n    sigma2 = np.atleast_2d(sigma2)\n\n    assert mu1.shape == mu2.shape, \"Training and test mean vectors have different lengths\"\n    assert sigma1.shape == sigma2.shape, \"Training and test covariances have different dimensions\"\n\n    diff = mu1 - mu2\n\n    # product might be almost singular\n    covmean, _ = linalg.sqrtm(sigma1.dot(sigma2), disp=False)\n    if not np.isfinite(covmean).all():\n        msg = \"fid calculation produces singular product; adding %s to diagonal of cov estimates\" % eps\n        warnings.warn(msg)\n        offset = np.eye(sigma1.shape[0]) * eps\n        # covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))\n        covmean = linalg.sqrtm((sigma1 + offset).dot(sigma2 + offset))\n    \n    # numerical error might give slight imaginary component\n    if np.iscomplexobj(covmean):\n        if not np.allclose(np.diagonal(covmean).imag, 0, atol=1e-3):\n            m = np.max(np.abs(covmean.imag))\n            raise ValueError(\"Imaginary component {}\".format(m))\n        covmean = covmean.real\n\n    # covmean = tf.linalg.sqrtm(tf.linalg.matmul(sigma1,sigma2))\n\n    print('covmean.shape=',covmean.shape)\n    # tr_covmean = tf.linalg.trace(covmean)\n\n    tr_covmean = np.trace(covmean)\n    return diff.dot(diff) + np.trace(sigma1) + np.trace(sigma2) - 2 * tr_covmean\n    # return diff.dot(diff) + tf.linalg.trace(sigma1) + tf.linalg.trace(sigma2) - 2 * tr_covmean\n#-------------------------------------------------------------------------------\n\n\ndef calculate_activation_statistics(images, sess, model_name, batch_size=50, verbose=False):\n    \"\"\"Calculation of the statistics used by the FID.\n    Params:\n    -- images      : Numpy array of dimension (n_images, hi, wi, 3). The values\n                     must lie between 0 and 255.\n    -- sess        : current session\n    -- batch_size  : the images numpy array is split into batches with batch size\n                     batch_size. A reasonable batch size depends on the available hardware.\n    -- verbose     : If set to True and parameter out_step is given, the number of calculated\n                     batches is reported.\n    Returns:\n    -- mu    : The mean over samples of the activations of the pool_3 layer of\n               the incption model.\n    -- sigma : The covariance matrix of the activations of the pool_3 layer of\n               the incption model.\n    \"\"\"\n    act = get_activations(images, sess, model_name, batch_size, verbose)\n    mu = np.mean(act, axis=0)\n    sigma = np.cov(act, rowvar=False)\n    return mu, sigma, act\n    \ndef _handle_path_memorization(path, sess, model_name, is_checksize, is_check_png):\n    path = pathlib.Path(path)\n    files = list(path.glob('*.jpg')) + list(path.glob('*.png'))\n    imsize = model_params[model_name]['imsize']\n\n    # In production we don't resize input images. This is just for demo purpose. \n    x = np.array([np.array(img_read_checks(fn, imsize, is_checksize, imsize, is_check_png)) for fn in files])\n    m, s, features = calculate_activation_statistics(x, sess, model_name)\n    del x #clean up memory\n    return m, s, features\n\n# check for image size\ndef img_read_checks(filename, resize_to, is_checksize=False, check_imsize = 64, is_check_png = False):\n    im = Image.open(str(filename))\n    if is_checksize and im.size != (check_imsize,check_imsize):\n        raise KernelEvalException('The images are not of size '+str(check_imsize))\n    \n    if is_check_png and im.format != 'PNG':\n        raise KernelEvalException('Only PNG images should be submitted.')\n\n    if resize_to is None:\n        return im\n    else:\n        return im.resize((resize_to,resize_to),Image.ANTIALIAS)\n\ndef calculate_kid_given_paths(paths, model_name, model_path, feature_path=None, mm=[], ss=[], ff=[]):\n    ''' Calculates the KID of two paths. '''\n    tf.reset_default_graph()\n    create_model_graph(str(model_path))\n    with tf.Session() as sess:\n        sess.run(tf.global_variables_initializer())\n        m1, s1, features1 = _handle_path_memorization(paths[0], sess, model_name, is_checksize = True, is_check_png = True)\n        if len(mm) != 0:\n            m2 = mm\n            s2 = ss\n            features2 = ff\n        elif feature_path is None:\n            m2, s2, features2 = _handle_path_memorization(paths[1], sess, model_name, is_checksize = False, is_check_png = False)\n        else:\n            with np.load(feature_path) as f:\n                m2, s2, features2 = f['m'], f['s'], f['features']\n\n        print('m1,m2 shape=',(m1.shape,m2.shape),'s1,s2=',(s1.shape,s2.shape))\n        print('starting calculating FID')\n        fid_value = calculate_frechet_distance(m1, s1, m2, s2)\n        print('done with FID, starting distance calculation')\n        distance = cosine_distance(features1, features2)        \n        return fid_value, distance, m2, s2, features2","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"if ComputeLB:\n  \n    # UNCOMPRESS OUR IMGAES\n    with zipfile.ZipFile(\"../working/images.zip\",\"r\") as z:\n        z.extractall(\"../tmp/images2/\")\n\n    # COMPUTE LB SCORE\n    m2 = []; s2 =[]; f2 = []\n    user_images_unzipped_path = '../tmp/images2/'\n    images_path = [user_images_unzipped_path,'../input/generative-dog-images/all-dogs/all-dogs/']\n    public_path = '../input/dog-face-generation-competition-kid-metric-input/classify_image_graph_def.pb'\n\n    fid_epsilon = 10e-15\n\n    fid_value_public, distance_public, m2, s2, f2 = calculate_kid_given_paths(images_path, 'Inception', public_path, mm=m2, ss=s2, ff=f2)\n    distance_public = distance_thresholding(distance_public, model_params['Inception']['cosine_distance_eps'])\n    print(\"FID_public: \", fid_value_public, \"distance_public: \", distance_public, \"multiplied_public: \",\n            fid_value_public /(distance_public + fid_epsilon))\n    \n    # REMOVE FILES TO PREVENT KERNEL ERROR OF TOO MANY FILES\n    ! rm -r ../tmp","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.4","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}