{"cells":[{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"import os\nimport math\nimport time\nfrom typing import *\nimport zipfile\nimport shutil\n\nfrom keras.models import Sequential, load_model, Model\nfrom keras.layers import Dense, Conv2D, Conv2DTranspose, Flatten, Reshape\nfrom keras.layers import UpSampling2D, Activation, BatchNormalization\nfrom keras.layers import LeakyReLU, Dropout, MaxPooling2D, Input, ZeroPadding2D\nfrom keras.initializers import TruncatedNormal, RandomNormal\nfrom keras.optimizers import Adam, RMSprop\nimport keras.backend as K\nimport tensorflow as tf\n\nimport xml.etree.ElementTree as ET\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom tqdm.auto import tqdm\nfrom PIL import Image\nimport cv2\nimport imgaug as ia\nfrom imgaug import augmenters as iaa\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"project_dir = './'\ndata_dir = '/kaggle/input/'\nimage_dir = f'{data_dir}/all-dogs/all-dogs/'\nannotations_dir = f'{data_dir}/annotation/Annotation/'\ncrop_dir = f'{project_dir}/crop/'\nmodel_dir = f'{project_dir}/saved_models/'\ngenerated_dir = f'{project_dir}/generated_images/'\n\nimg_shape = (64, 64, 3)\nbatch_size = 64\ntrain_steps = 32000 * 8 # 25_000 # 7200\nsave_interval = 1000 * 8","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Utils. Prepare data and project structure"},{"metadata":{"trusted":true},"cell_type":"code","source":"def make_dirs(dirs):\n    \"\"\"\n    :param List[str] dirs: list with directories to make \n        like ['folder/new_folder', 'another_new_folder/']\n    \"\"\"\n    for directory in dirs:\n        try:\n            os.mkdir(directory)\n            print(f'{directory} created!')\n        except FileExistsError as err:\n            print(err)\n        \n        \ndef delete_directory(directory: str):\n    if os.path.exists(directory):\n        shutil.rmtree(directory)\n        print(f'{directory} deleted!')\n    else:\n        print(f\"{directory} doesn't exist\")\n        \n            \ndef get_image(image_path: str, mode: str = 'RGB',\n              img_size: Tuple[int] = (64, 64, 3)) -> np.ndarray:\n    \"\"\"\n    Read image from image_path.\n    \n    :param image_path: Path of image\n    :param mode: Mode of image\n    :return: Image data as numpy.ndarray\n    \"\"\"\n    image = Image.open(image_path)\n    # image = image.resize((img_size[0], img_size[1]), Image.ANTIALIAS)\n    image = np.array(image.convert(mode))\n    image = image / 127.5 - 1.\n    return image\n\n\ndef get_batch(image_files: List[str], image_dir: str,\n              mode: str = 'RGB', img_size: Tuple[int] = (64, 64, 3)) -> np.ndarray:\n    \"\"\"\n    Load batch of pictures from directory.\n    \"\"\"\n    data_batch = []\n    for file in image_files:\n        image_path = os.path.join(image_dir, file)\n        image = get_image(image_path, mode, img_size=img_size)\n        data_batch.append(image)\n    data_batch = np.array(data_batch)\n    return data_batch\n\n\ndef train_generator_drive(image_dir: str, batch_size: int = 128, \n                    shuffle: bool = True, img_size: Tuple[int] = (64, 64, 3)) -> np.ndarray:\n    \"\"\"\n    Generator for training neural network.\n    \"\"\"\n    image_files = os.listdir(image_dir)\n    n_steps = math.floor(len(image_files) / batch_size) # all batches must be full\n    print(f'Founded {len(image_files)} pictures')\n    \n    if shuffle:\n        np.random.shuffle(image_files)\n        \n    while True:\n        for i in range(n_steps):\n            batch_img = image_files[i * batch_size: (i + 1) * batch_size]\n            batch_img = get_batch(batch_img, image_dir, img_size=img_size)\n            yield batch_img\n            \n\ndef train_generator(image_dir: str, batch_size: int = 128, \n                    shuffle: bool = True, img_size: Tuple[int] = (64, 64, 3)) -> np.ndarray:\n    \"\"\"\n    Generator for training neural network. Stores data in RAM.\n    \"\"\"\n    image_files = os.listdir(image_dir)\n    n_steps = math.floor(len(image_files) / batch_size) # all batches must be full\n    images = get_batch(image_files, image_dir, img_size=img_size)\n    print(f'Founded {len(images)} pictures')\n    \n    if shuffle:\n        np.random.shuffle(images)\n        \n    while True:\n        for i in range(n_steps):\n            batch_img = images[i * batch_size: (i + 1) * batch_size]\n            yield batch_img\n\n            \ndef pad_square(image):\n    \"\"\"\n    :param np.ndarray image: shape = (height, width, n_channels)\n    :rtype: np.ndarray\n    \"\"\"\n    old_size = image.shape[:2] # old_size is in (height, width) format\n    desired_size = max(old_size)\n    # desired_size = 64\n    \n    ratio = float(desired_size) / max(old_size)\n    new_size = tuple([int(x * ratio) for x in old_size])\n    # new_size should be in (width, height) format\n    image = cv2.resize(image, (new_size[1], new_size[0]),\n                       interpolation=cv2.INTER_AREA)\n    \n    delta_w = desired_size - new_size[1]\n    delta_h = desired_size - new_size[0]\n    top, bottom = delta_h // 2, delta_h // 2\n    left, right = delta_w // 2, delta_w // 2\n    \n    color = [0, 0, 0]\n    image = cv2.copyMakeBorder(image, top, bottom, left, right,\n                               cv2.BORDER_CONSTANT, value=color)\n    return image\n\n\ndef crop_square(image):\n    \"\"\"\n    :param np.ndarray image: shape = (height, width, n_channels)\n    :rtype: np.ndarray\n    \"\"\"\n    height, width = image.shape[: -1]  # Get dimensions\n    min_dim = min(width, height)\n\n    x1 = (width - min_dim) // 2\n    y1 = (height - min_dim) // 2\n    x2 = (width + min_dim) // 2\n    y2 = (height + min_dim) // 2\n\n    # Crop the center of the image\n    image = image[y1: y2, x1: x2]\n    # resize for test interpolation\n    # image = cv2.resize(image, (64, 64), interpolation=cv2.INTER_AREA)\n    # image = cv2.resize(image, (64, 64))\n    return image\n\n\ndef show_images(images, n_rows=5, n_cols=8):\n    \"\"\"\n    Show batch of images.\n    \n    :param np.ndarray images: shape = (n_images, height, width, channels)\n        or (height, width, channels) if one image.\n    \"\"\"\n    # check dimention\n    if len(images.shape) == 3:\n        images = np.expand_dims(images, axis=0)\n    plt.close('all')\n    plt.figure(figsize=(18, 10))\n    \n    for i in range(images.shape[0]):\n        plt.subplot(n_rows, n_cols, i + 1)\n        image = images[i]\n        plt.imshow(image)\n        # plt.axis('off')\n    plt.tight_layout()\n    plt.show()\n    plt.close('all')\n    \n\ndef augmentation(images, n_transform=3):\n    original_images = images.copy()\n    result = list()\n\n    transformations = []\n    for i in range (n_transform):\n        # include all possible changes\n        transform = iaa.SomeOf((1, 1), [\n            # iaa.Affine(rotate=(-30, 30), cval=0),\n            # iaa.Affine(shear=(0, 15)),\n            # iaa.Affine(rotate=(-15, 0), cval=0),\n            # iaa.Affine(rotate=(0, 20), mode=ia.ALL, cval=(0, 1)),\n            iaa.Fliplr(1),\n            # iaa.Affine(translate_percent={\"x\": (-0.1, 0.1), \"y\": (-0.2, 0.2)},\n            #       mode=ia.ALL, cval=(0, 255)),\n            # iaa.Affine(scale={\"x\": (0.7, 1.4), \"y\": (0.7, 1.4)},\n            #        mode=ia.ALL, cval=(0, 255)),\n            # iaa.Affine(shear=(-30, 30), mode=ia.ALL, cval=(0, 255)),\n            # Blur\n            # iaa.GaussianBlur(sigma=(0.0, 2.0)),\n            # iaa.AverageBlur(k=3),\n            # iaa.MedianBlur(k=3),\n            # iaa.AdditiveGaussianNoise(scale=0.05*255),\n            # iaa.ElasticTransformation(alpha=(0, 3.0), sigma=0.4), \n            # Others\n            # iaa.CropAndPad(percent=(-0.1, 0.1)),\n            # iaa.Dropout(p=(0.005, 0.01)),\n            # iaa.PiecewiseAffine(scale=(0.03, 0.05)),\n            # iaa.Sharpen(alpha=(0.0, 1.0), lightness=(0.75, 2.0)),\n            # iaa.Emboss(alpha=(0.0, 1.0), strength=(0.5, 1.5)),\n            ], random_order=True)\n        transformations.append(transform)\n    \n    for transform in transformations:\n        aug_images = transform.augment_images(original_images)\n        result.extend(aug_images)\n\n    result = list(images) + result \n    return np.array(result)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"delete_directory(crop_dir)\nmake_dirs([model_dir, generated_dir, crop_dir])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Crop images"},{"metadata":{"trusted":true},"cell_type":"code","source":"images = os.listdir(image_dir)\nbreeds = os.listdir(annotations_dir)\nskip_count = 0\nskip_small_dogs = 0\n\nfor breed in tqdm(breeds):\n    for i, img_name in enumerate(os.listdir(f'{annotations_dir}/{breed}')):\n        image = cv2.imread(os.path.join(image_dir, img_name + '.jpg'))\n        if image is None:\n            continue\n        tree = ET.parse(annotations_dir + breed + '/' + img_name)\n        myroot = tree.getroot()\n        objects = myroot.findall('object')\n        \n        if len(objects) > 1:\n            # print('More then one dog on the image. Skip', img_name)\n            skip_count += 1\n            continue\n    \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            # check dog area on the image\n            h_dog = ymax - ymin\n            w_dog = xmax - xmin\n            # square_dog = h_dog * w_dog\n            # square_img = image.shape[0] * image.shape[1]\n            # sq_threshold = 0.6\n            # if (square_dog / square_img) < sq_threshold:\n            #     # print(f'Dog square < {sq_threshold}. Img:', img_name)\n            #     skip_small_dogs += 1\n            #     continue\n            try: \n                image = image[ymin: ymax, xmin: xmax]\n            except TypeError: \n                continue\n                \n            if h_dog < 64 or w_dog <= 64:\n                skip_small_dogs += 1\n                continue\n                \n            w = image.shape[1]; h = image.shape[0];    \n            if w < h:\n                w2 = 64; h2 = int((64/w)*h)\n                image = cv2.resize(image, (w2, h2), interpolation=cv2.INTER_AREA)\n                image = image[0: 64, 0: 64]\n            else:\n                h2 = 64; w2 = int((64/h)*w)\n                image = crop_square(image)\n                image = cv2.resize(image, (64, 64), interpolation=cv2.INTER_AREA)\n        \n            # crop center square from image\n            # image = crop_square(image)\n            # crop upper part of image\n            # image = image[0: 64, 0: 64]\n            # print(image.shape)\n            \n            # image = cv2.resize(image, (64, 64), interpolation=cv2.INTER_AREA)\n            image = np.expand_dims(image, axis=0)\n            augmented = augmentation(image, n_transform=1)\n            assert augmented.shape == (2, 64, 64, 3)\n            for j, image in enumerate(augmented):\n                cv2.imwrite(os.path.join(crop_dir, img_name + f'_{j}.jpg'), image)\nprint('Skipped with several dogs =', skip_count) \nprint('Skipped with too small dogs =', skip_small_dogs) \nprint('Prepared =', len(os.listdir(crop_dir)))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Check train generator"},{"metadata":{"trusted":true},"cell_type":"code","source":"# 0.5 dog area remain\ngen = train_generator(crop_dir, batch_size=48)\nm = next(gen)\nprint(m.shape)\nm = m * 0.5 + 0.5\n\nshow_images(m, n_rows=6, n_cols=8)\n\ndel gen","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# check augmentation\ngen = train_generator_drive(image_dir, batch_size=8)\nm = next(gen)\nprint(m.shape)\nm = m * 0.5 + 0.5\nm = np.array(list(map(pad_square, m)))\nm = np.array(list(map(lambda x: cv2.resize(x, (64, 64), interpolation=cv2.INTER_AREA), m)))\nm = augmentation(m, n_transform=2)\n\nprint(m.shape)\nshow_images(m)\ndel gen","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Deep convolutional GAN"},{"metadata":{"trusted":true},"cell_type":"code","source":"# adapted from keras.optimizers.Adam\nclass AdamWithWeightnorm(Adam):\n    def get_updates(self, loss, params):\n        grads = self.get_gradients(loss, params)\n        self.updates = [K.update_add(self.iterations, 1)]\n\n        lr = self.lr\n        if self.initial_decay > 0:\n            lr *= (1. / (1. + self.decay * K.cast(self.iterations, K.floatx())))\n\n        t = K.cast(self.iterations + 1, K.floatx())\n        lr_t = lr * K.sqrt(1. - K.pow(self.beta_2, t)) / (1. - K.pow(self.beta_1, t))\n\n        shapes = [K.get_variable_shape(p) for p in params]\n        ms = [K.zeros(shape) for shape in shapes]\n        vs = [K.zeros(shape) for shape in shapes]\n        self.weights = [self.iterations] + ms + vs\n\n        for p, g, m, v in zip(params, grads, ms, vs):\n\n            # if a weight tensor (len > 1) use weight normalized parameterization\n            # this is the only part changed w.r.t. keras.optimizers.Adam\n            ps = K.get_variable_shape(p)\n            if len(ps)>1:\n\n                # get weight normalization parameters\n                V, V_norm, V_scaler, g_param, grad_g, grad_V = get_weightnorm_params_and_grads(p, g)\n\n                # Adam containers for the 'g' parameter\n                V_scaler_shape = K.get_variable_shape(V_scaler)\n                m_g = K.zeros(V_scaler_shape)\n                v_g = K.zeros(V_scaler_shape)\n\n                # update g parameters\n                m_g_t = (self.beta_1 * m_g) + (1. - self.beta_1) * grad_g\n                v_g_t = (self.beta_2 * v_g) + (1. - self.beta_2) * K.square(grad_g)\n                new_g_param = g_param - lr_t * m_g_t / (K.sqrt(v_g_t) + self.epsilon)\n                self.updates.append(K.update(m_g, m_g_t))\n                self.updates.append(K.update(v_g, v_g_t))\n\n                # update V parameters\n                m_t = (self.beta_1 * m) + (1. - self.beta_1) * grad_V\n                v_t = (self.beta_2 * v) + (1. - self.beta_2) * K.square(grad_V)\n                new_V_param = V - lr_t * m_t / (K.sqrt(v_t) + self.epsilon)\n                self.updates.append(K.update(m, m_t))\n                self.updates.append(K.update(v, v_t))\n\n                # if there are constraints we apply them to V, not W\n                if getattr(p, 'constraint', None) is not None:\n                    new_V_param = p.constraint(new_V_param)\n\n                # wn param updates --> W updates\n                add_weightnorm_param_updates(self.updates, new_V_param, new_g_param, p, V_scaler)\n\n            else: # do optimization normally\n                m_t = (self.beta_1 * m) + (1. - self.beta_1) * g\n                v_t = (self.beta_2 * v) + (1. - self.beta_2) * K.square(g)\n                p_t = p - lr_t * m_t / (K.sqrt(v_t) + self.epsilon)\n\n                self.updates.append(K.update(m, m_t))\n                self.updates.append(K.update(v, v_t))\n\n                new_p = p_t\n                # apply constraints\n                if getattr(p, 'constraint', None) is not None:\n                    new_p = p.constraint(new_p)\n                self.updates.append(K.update(p, new_p))\n        return self.updates\n\ndef get_weightnorm_params_and_grads(p, g):\n    ps = K.get_variable_shape(p)\n\n    # construct weight scaler: V_scaler = g/||V||\n    V_scaler_shape = (ps[-1],)  # assumes we're using tensorflow!\n    V_scaler = K.ones(V_scaler_shape)  # init to ones, so effective parameters don't change\n\n    # get V parameters = ||V||/g * W\n    norm_axes = [i for i in range(len(ps) - 1)]\n    V = p / tf.reshape(V_scaler, [1] * len(norm_axes) + [-1])\n\n    # split V_scaler into ||V|| and g parameters\n    V_norm = tf.sqrt(tf.reduce_sum(tf.square(V), norm_axes))\n    g_param = V_scaler * V_norm\n\n    # get grad in V,g parameters\n    grad_g = tf.reduce_sum(g * V, norm_axes) / V_norm\n    grad_V = tf.reshape(V_scaler, [1] * len(norm_axes) + [-1]) * \\\n             (g - tf.reshape(grad_g / V_norm, [1] * len(norm_axes) + [-1]) * V)\n\n    return V, V_norm, V_scaler, g_param, grad_g, grad_V\n\ndef add_weightnorm_param_updates(updates, new_V_param, new_g_param, W, V_scaler):\n    ps = K.get_variable_shape(new_V_param)\n    norm_axes = [i for i in range(len(ps) - 1)]\n\n    # update W and V_scaler\n    new_V_norm = tf.sqrt(tf.reduce_sum(tf.square(new_V_param), norm_axes))\n    new_V_scaler = new_g_param / new_V_norm\n    new_W = tf.reshape(new_V_scaler, [1] * len(norm_axes) + [-1]) * new_V_param\n    updates.append(K.update(W, new_W))\n    updates.append(K.update(V_scaler, new_V_scaler))\n\n# data based initialization for a given Keras model\ndef data_based_init(model, input):\n    # input can be dict, numpy array, or list of numpy arrays\n    if type(input) is dict:\n        feed_dict = input\n    elif type(input) is list:\n        feed_dict = {tf_inp: np_inp for tf_inp,np_inp in zip(model.inputs,input)}\n    else:\n        feed_dict = {model.inputs[0]: input}\n\n    # add learning phase if required\n    if model.uses_learning_phase and K.learning_phase() not in feed_dict:\n        feed_dict.update({K.learning_phase(): 1})\n\n    # get all layer name, output, weight, bias tuples\n    layer_output_weight_bias = []\n    for l in model.layers:\n        trainable_weights = l.trainable_weights\n        if len(trainable_weights) == 2:\n            W,b = trainable_weights\n            assert(l.built)\n            layer_output_weight_bias.append((l.name,l.get_output_at(0),W,b)) # if more than one node, only use the first\n\n    # iterate over our list and do data dependent init\n    sess = K.get_session()\n    for l,o,W,b in layer_output_weight_bias:\n        print('Performing data dependent initialization for layer ' + l)\n        m,v = tf.nn.moments(o, [i for i in range(len(o.get_shape())-1)])\n        s = tf.sqrt(v + 1e-10)\n        updates = tf.group(W.assign(W/tf.reshape(s,[1]*(len(W.get_shape())-1)+[-1])), b.assign((b-m)/s))\n        sess.run(updates, feed_dict)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"class DCGAN:\n    \n    def __init__(self, img_shape):\n        self.img_shape = img_shape\n        self.noise_size = 100\n        \n        self.discriminator = self.build_discriminator()\n        self.generator = self.build_generator()\n        self.discriminator_model = self.build_discriminator_model()\n        self.adversarial_model = self.build_adversarial_model()\n        \n    def build_discriminator(self):\n        filters = 32\n        dropout = 0.25 # 0.25\n        model = Sequential(name='Discriminator')\n        \n        # In: 64 x 64 x 3, channels(filters) = 3\n        # Out: 32 x 32 x 32\n        model.add(Conv2D(filters, kernel_size=3, strides=2,\n                  input_shape=self.img_shape, padding='same'))\n        model.add(LeakyReLU(alpha=0.2))\n        model.add(Dropout(dropout))\n        \n        # In: 32 x 32 x 32\n        # Out: 18 x 18 x 64\n        model.add(Conv2D(filters * 2, kernel_size=3, strides=2, padding='same'))\n        model.add(ZeroPadding2D(padding=((0,1),(0,1))))\n        model.add(BatchNormalization(momentum=0.8))\n        model.add(LeakyReLU(alpha=0.2))\n        model.add(Dropout(dropout))\n        \n        # In: 18 x 18 x 64\n        # Out: 9 x 9 x 128\n        model.add(Conv2D(filters * 4, 3, strides=2, padding='same'))\n        model.add(BatchNormalization(momentum=0.8))\n        model.add(LeakyReLU(alpha=0.2))\n        model.add(Dropout(dropout))\n        \n        # In: 9 x 9 x 128\n        # Out: 9 x 9 x 256\n        model.add(Conv2D(filters * 8, 3, strides=1, padding='same'))\n        model.add(BatchNormalization(momentum=0.8))\n        model.add(LeakyReLU(alpha=0.2))\n        model.add(Dropout(dropout))\n        \n        # In: 9 x 9 x 256\n        # Out: 9 x 9 x 512\n        model.add(Conv2D(filters * 16, 3, strides=1, padding='same'))\n        model.add(BatchNormalization(momentum=0.8))\n        model.add(LeakyReLU(alpha=0.2))\n        model.add(Dropout(dropout))\n        \n        # In: 9 x 9 x 512, filters = 512\n        # Out: 1-dim probability\n        model.add(Flatten())\n        model.add(Dense(1))\n        model.add(Activation('sigmoid'))\n        \n        # input_image = Input(shape=self.img_shape)\n        # validity = model(input_image)\n        # model = Model(input_image, validity)\n        # model.name = 'Discriminator'\n        \n        print('\\nDiscriminator summary (non compiled):')\n        model.summary()\n        \n        return model\n    \n    def build_generator(self):\n        # dropout = 0.5 # 0.4 # дропаут в G все ломает\n        filters = 64 * 4\n        dim = 4\n        model = Sequential(name='Generator')\n        \n        # In: 100\n        # Out: dim x dim x filters\n        model.add(Dense(dim * dim * filters, input_dim=self.noise_size))\n        # model.add(BatchNormalization(momentum=0.9))\n        # model.add(Activation('relu'))\n        model.add(LeakyReLU(alpha=0.2))\n        model.add(Reshape((dim, dim, filters)))\n        # model.add(Dropout(dropout))\n        \n        # In: dim x dim x filters\n        # Out: 2*dim x 2*dim x filters\n        model.add(UpSampling2D())\n        model.add(Conv2D(filters, 3, padding='same'))\n        model.add(BatchNormalization(momentum=0.8))\n        model.add(LeakyReLU(alpha=0.2))\n        # model.add(Activation('relu'))\n        # model.add(Dropout(dropout))\n        \n        # In: 2*dim x 2*dim x filters\n        # Out: 4*dim x 4*dim x filters\n        model.add(UpSampling2D())\n        model.add(Conv2D(filters, 3, padding='same'))\n        model.add(BatchNormalization(momentum=0.8))\n        model.add(LeakyReLU(alpha=0.2))\n        # model.add(Activation('relu'))\n        # model.add(Dropout(dropout))\n        \n        # In: 4*dim x 4*dim x filters\n        # Out: 8*dim x 8*dim x filters/2\n        model.add(UpSampling2D())\n        model.add(Conv2D(int(filters / 2), 3, padding='same'))\n        model.add(BatchNormalization(momentum=0.8))\n        model.add(LeakyReLU(alpha=0.2))\n        # model.add(Activation('relu'))\n        # model.add(Dropout(dropout))\n        \n        # In: 4*dim x 4*dim x filters/2\n        # Out: 16*dim x 16*dim x filters/2\n        model.add(UpSampling2D())\n        model.add(Conv2D(int(filters / 2), 3, padding='same'))\n        model.add(BatchNormalization(momentum=0.8))\n        model.add(LeakyReLU(alpha=0.2))\n        # model.add(Activation('relu'))\n        # model.add(Dropout(dropout))\n        \n        # In: 16*dim x 16*dim x filters/2\n        # Out: 64 x 64 x 3 grayscale image [-1.0, 1.0] per pix\n        model.add(Conv2D(self.img_shape[-1], 3, padding='same'))\n        model.add(Activation('tanh'))\n        \n        # input = Input(shape=(self.noise_size,))\n        # generated_image = model(input)\n        # model = Model(input, generated_image)\n        # model.name = 'Generator'\n        \n        print('\\nGenerator summary (non compiled):')\n        model.summary()\n        \n        return model\n    \n    def build_discriminator_model(self):\n        \n        # optimizer = RMSprop(lr=0.0002, decay=6e-8)\n        # optimizer = Adam(1.5e-4, 0.5)\n        optimizer = AdamWithWeightnorm(lr = 0.0002, beta_1 = 0.5)\n        \n        model = Sequential(name='Discriminator_model')\n        model.add(self.discriminator)\n        # model = self.discriminator\n        model.compile(loss='binary_crossentropy', optimizer=optimizer,\n                      metrics=['accuracy'])\n        \n        print('\\nDiscriminator model summary (compiled):')\n        model.summary()\n        return model\n    \n    def build_adversarial_model(self):\n        \n        # optimizer = RMSprop(lr=0.0001, decay=3e-8)\n        # optimizer = Adam(1.5e-4, 0.5)\n        optimizer = AdamWithWeightnorm(lr = 0.0002, beta_1 = 0.5)\n        \n        # random_input = Input(shape=(self.noise_size,))\n        # generated_image = self.generator(random_input)\n        # \n        # self.discriminator_model.trainable = False\n        # \n        # validity = self.discriminator_model(generated_image)\n        # \n        # model = Model(random_input, validity)\n        # model.name = 'Adversatial_model'\n        # model.compile(loss='binary_crossentropy', optimizer=optimizer,\n        #               metrics=['accuracy'])\n        \n        model = Sequential(name='Adversatial_model')\n        model.add(self.generator)\n        self.discriminator_model.trainable = False\n        model.add(self.discriminator_model)\n        model.compile(loss='binary_crossentropy', optimizer=optimizer,\n                      metrics=['accuracy'])\n        \n        print('\\nAdversarial model summary (compiled):')\n        model.summary()\n        return model\n    \n    def train(self, model_dir, images_dir, train_generator, \n              train_steps=1000, batch_size=256, save_interval=0):\n        y_real = np.random.random((batch_size, 1)) * (1.2 - 0.7) + 0.7 # np.ones((batch_size, 1))\n        y_fake = np.random.random((batch_size, 1)) * 0.3 # np.zeros((batch_size, 1))\n        \n        for i in tqdm(range(1, train_steps + 1), desc='Training'):\n            x_real = next(train_generator) \n            noise = np.random.normal(0, 1, size=(batch_size, self.noise_size))\n            x_fake = self.generator.predict(noise)\n                  \n            # X = np.concatenate((x_real, x_fake))\n            # Y = np.concatenate((y_real, y_fake))\n            prob = np.random.random()\n            if prob >= 0.5:\n                d_loss_real = self.discriminator_model.train_on_batch(x_real, y_real)\n                d_loss_fake = self.discriminator_model.train_on_batch(x_fake, y_fake)\n            else:\n                d_loss_fake = self.discriminator_model.train_on_batch(x_fake, y_fake)\n                d_loss_real = self.discriminator_model.train_on_batch(x_real, y_real)\n                \n            d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)\n            \n            a_loss = self.adversarial_model.train_on_batch(noise, y_real)\n            \n            if (save_interval > 0) and (i % save_interval == 0):\n                self.generator.save(os.path.join(model_dir, 'generator.h5'))\n                self.discriminator.save(os.path.join(model_dir, \n                                                     'discriminator.h5'))\n                log_mesg = \"%d: [D loss: %f, acc: %f]\" % (i, d_loss[0], d_loss[1])\n                log_mesg = \"%s  [A loss: %f, acc: %f]\" % (log_mesg, a_loss[0], a_loss[1])\n                print(log_mesg)\n                img_path = os.path.join(images_dir, f'mnist_step_{i}.png')\n                gen_images = self.generate_batch_images(24)\n                self.save_images(gen_images, img_path, show=True)           \n        \n    def generate_image(self, noise=None):\n        \"\"\"\n        :param np.ndarray noise: shape = [1, noise_size]\n        \n        :return: image array of shape (height, width, n_channels).\n        :rtype: np.ndarray\n        \"\"\"\n        return self.generate_batch_images(n_images=1, noise=noise)[0]\n            \n    def generate_batch_images(self, n_images, noise=None):\n        \"\"\"\n        :param int n_images: not used if noise is given.\n        :param np.ndarray noise: shape = [n_images, noise_size].\n        \n        :return: images array of shape (n_images, height, width, n_channels)\n        :rtype: np.ndarray\n        \"\"\"\n        if not noise:\n            noise = np.random.normal(0, 1, size=(n_images, self.noise_size))\n        images = self.generator.predict(noise)\n        images = 0.5 * images + 0.5\n        return images\n    \n    def save_images(self, images, file_path, n_rows=4, n_cols=6, show=False):\n        \"\"\"\n        Save batch of images as one file.\n        \n        :param np.ndarray images: shape = (n_images, height, width, channels)\n            or (height, width, channels) if one image.\n        \"\"\"\n        # check dimention\n        if len(images.shape) == 3:\n            images.resize(1, images.shape[0], images.shape[1], images.shape[2])\n        plt.close('all')\n        plt.figure(figsize=(10, 10))\n        \n        for i in range(images.shape[0]):\n            plt.subplot(n_rows, n_cols, i + 1)\n            image = images[i, :, :, :]\n            image = np.reshape(image, self.img_shape)\n            plt.imshow(image)\n            plt.axis('off')\n        plt.tight_layout()\n        plt.savefig(file_path)\n        \n        if show:\n            plt.show()\n        plt.close('all')\n        \n    def load_generator(self, generator_path):\n        \"\"\"\n        Loads generator from *.h5 file.\n        \"\"\"\n        self.generator = load_model(generator_path)\n        ","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Train GAN"},{"metadata":{"trusted":true},"cell_type":"code","source":"gan = DCGAN(img_shape=img_shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"try:\n    del train_gen\nexcept NameError:\n    print(\"Doesn't exist\")\ntrain_gen = train_generator(crop_dir, batch_size=batch_size)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n\ngan.train(model_dir, generated_dir, train_gen, train_steps=train_steps, \n          batch_size=batch_size, save_interval=save_interval)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Overview final generator"},{"metadata":{"trusted":true},"cell_type":"code","source":"images = gan.generate_batch_images(30)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"gan.save_images(images, f'{generated_dir}/test_generator.png',\n                n_rows=5, show=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Save submission"},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\nz = zipfile.PyZipFile('images.zip', mode='w')\n\nfor i in tqdm(range(10_000), desc='Generate submission'):\n    img = gan.generate_image()\n    file = str(i) +'.png'\n    # plt.imshow(img)\n    # plt.savefig(file)\n    # plt.close('all')\n    img = (img * 255).astype(np.uint8)\n    img = Image.fromarray(img)\n    img.save(file,'PNG') # Image object\n    z.write(file)\n    os.remove(file)\n    \nz.close()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"delete_directory(crop_dir)","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}