[
  {
    "path": ".gitignore",
    "content": "# Data\ndata\nsamples\n*.zip\nlogs\ntest*\n\nweb/js/gen_layers.js\n\n# checkpoint\ncheckpoint\n\n# trash\n.dropbox\n.DS_Store\n\n# Created by https://www.gitignore.io/api/python,vim\n\n### Python ###\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nenv/\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\n*.egg-info/\n.installed.cfg\n*.egg\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*,cover\n.hypothesis/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n\n### Vim ###\n[._]*.s[a-w][a-z]\n[._]s[a-w][a-z]\n*.un~\nSession.vim\n.netrwhist\n*~\n\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 Taehoon Kim\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# DCGAN in Tensorflow\n\nTensorflow implementation of [Deep Convolutional Generative Adversarial Networks](http://arxiv.org/abs/1511.06434) which is a stabilize Generative Adversarial Networks. The referenced torch code can be found [here](https://github.com/soumith/dcgan.torch).\n\n![alt tag](DCGAN.png)\n\n* [Brandon Amos](http://bamos.github.io/) wrote an excellent [blog post](http://bamos.github.io/2016/08/09/deep-completion/) and [image completion code](https://github.com/bamos/dcgan-completion.tensorflow) based on this repo.\n* *To avoid the fast convergence of D (discriminator) network, G (generator) network is updated twice for each D network update, which differs from original paper.*\n\n\n## Online Demo\n\n[<img src=\"https://raw.githubusercontent.com/carpedm20/blog/master/content/images/face.png\">](http://carpedm20.github.io/faces/)\n\n[link](http://carpedm20.github.io/faces/)\n\n\n## Prerequisites\n\n- Python 2.7 or Python 3.3+\n- [Tensorflow 0.12.1](https://github.com/tensorflow/tensorflow/tree/r0.12)\n- [SciPy](http://www.scipy.org/install.html)\n- [pillow](https://github.com/python-pillow/Pillow)\n- [tqdm](https://pypi.org/project/tqdm/)\n- (Optional) [moviepy](https://github.com/Zulko/moviepy) (for visualization)\n- (Optional) [Align&Cropped Images.zip](http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html) : Large-scale CelebFaces Dataset\n\n\n## Usage\n\nFirst, download dataset with:\n\n    $ python download.py mnist celebA\n\nTo train a model with downloaded dataset:\n\n    $ python main.py --dataset mnist --input_height=28 --output_height=28 --train\n    $ python main.py --dataset celebA --input_height=108 --train --crop\n\nTo test with an existing model:\n\n    $ python main.py --dataset mnist --input_height=28 --output_height=28\n    $ python main.py --dataset celebA --input_height=108 --crop\n\nOr, you can use your own dataset (without central crop) by:\n\n    $ mkdir data/DATASET_NAME\n    ... add images to data/DATASET_NAME ...\n    $ python main.py --dataset DATASET_NAME --train\n    $ python main.py --dataset DATASET_NAME\n    $ # example\n    $ python main.py --dataset=eyes --input_fname_pattern=\"*_cropped.png\" --train\n\nIf your dataset is located in a different root directory:\n\n    $ python main.py --dataset DATASET_NAME --data_dir DATASET_ROOT_DIR --train\n    $ python main.py --dataset DATASET_NAME --data_dir DATASET_ROOT_DIR\n    $ # example\n    $ python main.py --dataset=eyes --data_dir ../datasets/ --input_fname_pattern=\"*_cropped.png\" --train\n    \n\n## Results\n\n![result](assets/training.gif)\n\n### celebA\n\nAfter 6th epoch:\n\n![result3](assets/result_16_01_04_.png)\n\nAfter 10th epoch:\n\n![result4](assets/test_2016-01-27%2015:08:54.png)\n\n### Asian face dataset\n\n![custom_result1](web/img/change5.png)\n\n![custom_result1](web/img/change2.png)\n\n![custom_result2](web/img/change4.png)\n\n### MNIST\n\nMNIST codes are written by [@PhoenixDai](https://github.com/PhoenixDai).\n\n![mnist_result1](assets/mnist1.png)\n\n![mnist_result2](assets/mnist2.png)\n\n![mnist_result3](assets/mnist3.png)\n\nMore results can be found [here](./assets/) and [here](./web/img/).\n\n\n## Training details\n\nDetails of the loss of Discriminator and Generator (with custom dataset not celebA).\n\n![d_loss](assets/d_loss.png)\n\n![g_loss](assets/g_loss.png)\n\nDetails of the histogram of true and fake result of discriminator (with custom dataset not celebA).\n\n![d_hist](assets/d_hist.png)\n\n![d__hist](assets/d__hist.png)\n\n\n## Related works\n\n- [BEGAN-tensorflow](https://github.com/carpedm20/BEGAN-tensorflow)\n- [DiscoGAN-pytorch](https://github.com/carpedm20/DiscoGAN-pytorch)\n- [simulated-unsupervised-tensorflow](https://github.com/carpedm20/simulated-unsupervised-tensorflow)\n\n\n## Author\n\nTaehoon Kim / [@carpedm20](http://carpedm20.github.io/)\n"
  },
  {
    "path": "download.py",
    "content": "\"\"\"\nModification of https://github.com/stanfordnlp/treelstm/blob/master/scripts/download.py\n\nDownloads the following:\n- Celeb-A dataset\n- LSUN dataset\n- MNIST dataset\n\"\"\"\n\nfrom __future__ import print_function\nimport os\nimport sys\nimport gzip\nimport json\nimport shutil\nimport zipfile\nimport argparse\nimport requests\nimport subprocess\nfrom tqdm import tqdm\nfrom six.moves import urllib\n\nparser = argparse.ArgumentParser(description='Download dataset for DCGAN.')\nparser.add_argument('datasets', metavar='N', type=str, nargs='+', choices=['celebA', 'lsun', 'mnist'],\n           help='name of dataset to download [celebA, lsun, mnist]')\n\ndef download(url, dirpath):\n  filename = url.split('/')[-1]\n  filepath = os.path.join(dirpath, filename)\n  u = urllib.request.urlopen(url)\n  f = open(filepath, 'wb')\n  filesize = int(u.headers[\"Content-Length\"])\n  print(\"Downloading: %s Bytes: %s\" % (filename, filesize))\n\n  downloaded = 0\n  block_sz = 8192\n  status_width = 70\n  while True:\n    buf = u.read(block_sz)\n    if not buf:\n      print('')\n      break\n    else:\n      print('', end='\\r')\n    downloaded += len(buf)\n    f.write(buf)\n    status = ((\"[%-\" + str(status_width + 1) + \"s] %3.2f%%\") %\n      ('=' * int(float(downloaded) / filesize * status_width) + '>', downloaded * 100. / filesize))\n    print(status, end='')\n    sys.stdout.flush()\n  f.close()\n  return filepath\n\ndef download_file_from_google_drive(id, destination):\n  URL = \"https://docs.google.com/uc?export=download\"\n  session = requests.Session()\n\n  response = session.get(URL, params={ 'id': id }, stream=True)\n  token = get_confirm_token(response)\n\n  if token:\n    params = { 'id' : id, 'confirm' : token }\n    response = session.get(URL, params=params, stream=True)\n\n  save_response_content(response, destination)\n\ndef get_confirm_token(response):\n  for key, value in response.cookies.items():\n    if key.startswith('download_warning'):\n      return value\n  return None\n\ndef save_response_content(response, destination, chunk_size=32*1024):\n  total_size = int(response.headers.get('content-length', 0))\n  with open(destination, \"wb\") as f:\n    for chunk in tqdm(response.iter_content(chunk_size), total=total_size,\n              unit='B', unit_scale=True, desc=destination):\n      if chunk: # filter out keep-alive new chunks\n        f.write(chunk)\n\ndef unzip(filepath):\n  print(\"Extracting: \" + filepath)\n  dirpath = os.path.dirname(filepath)\n  with zipfile.ZipFile(filepath) as zf:\n    zf.extractall(dirpath)\n  os.remove(filepath)\n\ndef download_celeb_a(dirpath):\n  data_dir = 'celebA'\n  if os.path.exists(os.path.join(dirpath, data_dir)):\n    print('Found Celeb-A - skip')\n    return\n\n  filename, drive_id  = \"img_align_celeba.zip\", \"0B7EVK8r0v71pZjFTYXZWM3FlRnM\"\n  save_path = os.path.join(dirpath, filename)\n\n  if os.path.exists(save_path):\n    print('[*] {} already exists'.format(save_path))\n  else:\n    download_file_from_google_drive(drive_id, save_path)\n\n  zip_dir = ''\n  with zipfile.ZipFile(save_path) as zf:\n    zip_dir = zf.namelist()[0]\n    zf.extractall(dirpath)\n  os.remove(save_path)\n  os.rename(os.path.join(dirpath, zip_dir), os.path.join(dirpath, data_dir))\n\ndef _list_categories(tag):\n  url = 'http://lsun.cs.princeton.edu/htbin/list.cgi?tag=' + tag\n  f = urllib.request.urlopen(url)\n  return json.loads(f.read())\n\ndef _download_lsun(out_dir, category, set_name, tag):\n  url = 'http://lsun.cs.princeton.edu/htbin/download.cgi?tag={tag}' \\\n      '&category={category}&set={set_name}'.format(**locals())\n  print(url)\n  if set_name == 'test':\n    out_name = 'test_lmdb.zip'\n  else:\n    out_name = '{category}_{set_name}_lmdb.zip'.format(**locals())\n  out_path = os.path.join(out_dir, out_name)\n  cmd = ['curl', url, '-o', out_path]\n  print('Downloading', category, set_name, 'set')\n  subprocess.call(cmd)\n\ndef download_lsun(dirpath):\n  data_dir = os.path.join(dirpath, 'lsun')\n  if os.path.exists(data_dir):\n    print('Found LSUN - skip')\n    return\n  else:\n    os.mkdir(data_dir)\n\n  tag = 'latest'\n  #categories = _list_categories(tag)\n  categories = ['bedroom']\n\n  for category in categories:\n    _download_lsun(data_dir, category, 'train', tag)\n    _download_lsun(data_dir, category, 'val', tag)\n  _download_lsun(data_dir, '', 'test', tag)\n\ndef download_mnist(dirpath):\n  data_dir = os.path.join(dirpath, 'mnist')\n  if os.path.exists(data_dir):\n    print('Found MNIST - skip')\n    return\n  else:\n    os.mkdir(data_dir)\n  url_base = 'http://yann.lecun.com/exdb/mnist/'\n  file_names = ['train-images-idx3-ubyte.gz',\n                'train-labels-idx1-ubyte.gz',\n                't10k-images-idx3-ubyte.gz',\n                't10k-labels-idx1-ubyte.gz']\n  for file_name in file_names:\n    url = (url_base+file_name).format(**locals())\n    print(url)\n    out_path = os.path.join(data_dir,file_name)\n    cmd = ['curl', url, '-o', out_path]\n    print('Downloading ', file_name)\n    subprocess.call(cmd)\n    cmd = ['gzip', '-d', out_path]\n    print('Decompressing ', file_name)\n    subprocess.call(cmd)\n\ndef prepare_data_dir(path = './data'):\n  if not os.path.exists(path):\n    os.mkdir(path)\n\nif __name__ == '__main__':\n  args = parser.parse_args()\n  prepare_data_dir()\n\n  if any(name in args.datasets for name in ['CelebA', 'celebA', 'celebA']):\n    download_celeb_a('./data')\n  if 'lsun' in args.datasets:\n    download_lsun('./data')\n  if 'mnist' in args.datasets:\n    download_mnist('./data')\n"
  },
  {
    "path": "main.py",
    "content": "import os\nimport scipy.misc\nimport numpy as np\nimport json\n\nfrom model import DCGAN\nfrom utils import pp, visualize, to_json, show_all_variables, expand_path, timestamp\n\nimport tensorflow as tf\n\nflags = tf.app.flags\nflags.DEFINE_integer(\"epoch\", 25, \"Epoch to train [25]\")\nflags.DEFINE_float(\"learning_rate\", 0.0002, \"Learning rate of for adam [0.0002]\")\nflags.DEFINE_float(\"beta1\", 0.5, \"Momentum term of adam [0.5]\")\nflags.DEFINE_float(\"train_size\", np.inf, \"The size of train images [np.inf]\")\nflags.DEFINE_integer(\"batch_size\", 64, \"The size of batch images [64]\")\nflags.DEFINE_integer(\"input_height\", 108, \"The size of image to use (will be center cropped). [108]\")\nflags.DEFINE_integer(\"input_width\", None, \"The size of image to use (will be center cropped). If None, same value as input_height [None]\")\nflags.DEFINE_integer(\"output_height\", 64, \"The size of the output images to produce [64]\")\nflags.DEFINE_integer(\"output_width\", None, \"The size of the output images to produce. If None, same value as output_height [None]\")\nflags.DEFINE_string(\"dataset\", \"celebA\", \"The name of dataset [celebA, mnist, lsun]\")\nflags.DEFINE_string(\"input_fname_pattern\", \"*.jpg\", \"Glob pattern of filename of input images [*]\")\nflags.DEFINE_string(\"data_dir\", \"./data\", \"path to datasets [e.g. $HOME/data]\")\nflags.DEFINE_string(\"out_dir\", \"./out\", \"Root directory for outputs [e.g. $HOME/out]\")\nflags.DEFINE_string(\"out_name\", \"\", \"Folder (under out_root_dir) for all outputs. Generated automatically if left blank []\")\nflags.DEFINE_string(\"checkpoint_dir\", \"checkpoint\", \"Folder (under out_root_dir/out_name) to save checkpoints [checkpoint]\")\nflags.DEFINE_string(\"sample_dir\", \"samples\", \"Folder (under out_root_dir/out_name) to save samples [samples]\")\nflags.DEFINE_boolean(\"train\", False, \"True for training, False for testing [False]\")\nflags.DEFINE_boolean(\"crop\", False, \"True for training, False for testing [False]\")\nflags.DEFINE_boolean(\"visualize\", False, \"True for visualizing, False for nothing [False]\")\nflags.DEFINE_boolean(\"export\", False, \"True for exporting with new batch size\")\nflags.DEFINE_boolean(\"freeze\", False, \"True for exporting with new batch size\")\nflags.DEFINE_integer(\"max_to_keep\", 1, \"maximum number of checkpoints to keep\")\nflags.DEFINE_integer(\"sample_freq\", 200, \"sample every this many iterations\")\nflags.DEFINE_integer(\"ckpt_freq\", 200, \"save checkpoint every this many iterations\")\nflags.DEFINE_integer(\"z_dim\", 100, \"dimensions of z\")\nflags.DEFINE_string(\"z_dist\", \"uniform_signed\", \"'normal01' or 'uniform_unsigned' or uniform_signed\")\nflags.DEFINE_boolean(\"G_img_sum\", False, \"Save generator image summaries in log\")\n#flags.DEFINE_integer(\"generate_test_images\", 100, \"Number of images to generate during test. [100]\")\nFLAGS = flags.FLAGS\n\ndef main(_):\n  pp.pprint(flags.FLAGS.__flags)\n  \n  # expand user name and environment variables\n  FLAGS.data_dir = expand_path(FLAGS.data_dir)\n  FLAGS.out_dir = expand_path(FLAGS.out_dir)\n  FLAGS.out_name = expand_path(FLAGS.out_name)\n  FLAGS.checkpoint_dir = expand_path(FLAGS.checkpoint_dir)\n  FLAGS.sample_dir = expand_path(FLAGS.sample_dir)\n\n  if FLAGS.output_height is None: FLAGS.output_height = FLAGS.input_height\n  if FLAGS.input_width is None: FLAGS.input_width = FLAGS.input_height\n  if FLAGS.output_width is None: FLAGS.output_width = FLAGS.output_height\n\n  # output folders\n  if FLAGS.out_name == \"\":\n      FLAGS.out_name = '{} - {} - {}'.format(timestamp(), FLAGS.data_dir.split('/')[-1], FLAGS.dataset) # penultimate folder of path\n      if FLAGS.train:\n        FLAGS.out_name += ' - x{}.z{}.{}.y{}.b{}'.format(FLAGS.input_width, FLAGS.z_dim, FLAGS.z_dist, FLAGS.output_width, FLAGS.batch_size)\n\n  FLAGS.out_dir = os.path.join(FLAGS.out_dir, FLAGS.out_name)\n  FLAGS.checkpoint_dir = os.path.join(FLAGS.out_dir, FLAGS.checkpoint_dir)\n  FLAGS.sample_dir = os.path.join(FLAGS.out_dir, FLAGS.sample_dir)\n\n  if not os.path.exists(FLAGS.checkpoint_dir): os.makedirs(FLAGS.checkpoint_dir)\n  if not os.path.exists(FLAGS.sample_dir): os.makedirs(FLAGS.sample_dir)\n\n  with open(os.path.join(FLAGS.out_dir, 'FLAGS.json'), 'w') as f:\n    flags_dict = {k:FLAGS[k].value for k in FLAGS}\n    json.dump(flags_dict, f, indent=4, sort_keys=True, ensure_ascii=False)\n  \n\n  #gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333)\n  run_config = tf.ConfigProto()\n  run_config.gpu_options.allow_growth=True\n\n  with tf.Session(config=run_config) as sess:\n    if FLAGS.dataset == 'mnist':\n      dcgan = DCGAN(\n          sess,\n          input_width=FLAGS.input_width,\n          input_height=FLAGS.input_height,\n          output_width=FLAGS.output_width,\n          output_height=FLAGS.output_height,\n          batch_size=FLAGS.batch_size,\n          sample_num=FLAGS.batch_size,\n          y_dim=10,\n          z_dim=FLAGS.z_dim,\n          dataset_name=FLAGS.dataset,\n          input_fname_pattern=FLAGS.input_fname_pattern,\n          crop=FLAGS.crop,\n          checkpoint_dir=FLAGS.checkpoint_dir,\n          sample_dir=FLAGS.sample_dir,\n          data_dir=FLAGS.data_dir,\n          out_dir=FLAGS.out_dir,\n          max_to_keep=FLAGS.max_to_keep)\n    else:\n      dcgan = DCGAN(\n          sess,\n          input_width=FLAGS.input_width,\n          input_height=FLAGS.input_height,\n          output_width=FLAGS.output_width,\n          output_height=FLAGS.output_height,\n          batch_size=FLAGS.batch_size,\n          sample_num=FLAGS.batch_size,\n          z_dim=FLAGS.z_dim,\n          dataset_name=FLAGS.dataset,\n          input_fname_pattern=FLAGS.input_fname_pattern,\n          crop=FLAGS.crop,\n          checkpoint_dir=FLAGS.checkpoint_dir,\n          sample_dir=FLAGS.sample_dir,\n          data_dir=FLAGS.data_dir,\n          out_dir=FLAGS.out_dir,\n          max_to_keep=FLAGS.max_to_keep)\n\n    show_all_variables()\n\n    if FLAGS.train:\n      dcgan.train(FLAGS)\n    else:\n      load_success, load_counter = dcgan.load(FLAGS.checkpoint_dir)\n      if not load_success:\n        raise Exception(\"Checkpoint not found in \" + FLAGS.checkpoint_dir)\n\n\n    # to_json(\"./web/js/layers.js\", [dcgan.h0_w, dcgan.h0_b, dcgan.g_bn0],\n    #                 [dcgan.h1_w, dcgan.h1_b, dcgan.g_bn1],\n    #                 [dcgan.h2_w, dcgan.h2_b, dcgan.g_bn2],\n    #                 [dcgan.h3_w, dcgan.h3_b, dcgan.g_bn3],\n    #                 [dcgan.h4_w, dcgan.h4_b, None])\n\n    # Below is codes for visualization\n      if FLAGS.export:\n        export_dir = os.path.join(FLAGS.checkpoint_dir, 'export_b'+str(FLAGS.batch_size))\n        dcgan.save(export_dir, load_counter, ckpt=True, frozen=False)\n\n      if FLAGS.freeze:\n        export_dir = os.path.join(FLAGS.checkpoint_dir, 'frozen_b'+str(FLAGS.batch_size))\n        dcgan.save(export_dir, load_counter, ckpt=False, frozen=True)\n\n      if FLAGS.visualize:\n        OPTION = 1\n        visualize(sess, dcgan, FLAGS, OPTION, FLAGS.sample_dir)\n\nif __name__ == '__main__':\n  tf.app.run()\n"
  },
  {
    "path": "model.py",
    "content": "from __future__ import division\nfrom __future__ import print_function\nimport os\nimport time\nimport math\nfrom glob import glob\nimport tensorflow as tf\nimport numpy as np\nfrom six.moves import xrange\n\nfrom ops import *\nfrom utils import *\n\ndef conv_out_size_same(size, stride):\n  return int(math.ceil(float(size) / float(stride)))\n\ndef gen_random(mode, size):\n    if mode=='normal01': return np.random.normal(0,1,size=size)\n    if mode=='uniform_signed': return np.random.uniform(-1,1,size=size)\n    if mode=='uniform_unsigned': return np.random.uniform(0,1,size=size)\n\n\nclass DCGAN(object):\n  def __init__(self, sess, input_height=108, input_width=108, crop=True,\n         batch_size=64, sample_num = 64, output_height=64, output_width=64,\n         y_dim=None, z_dim=100, gf_dim=64, df_dim=64,\n         gfc_dim=1024, dfc_dim=1024, c_dim=3, dataset_name='default',\n         max_to_keep=1,\n         input_fname_pattern='*.jpg', checkpoint_dir='ckpts', sample_dir='samples', out_dir='./out', data_dir='./data'):\n    \"\"\"\n\n    Args:\n      sess: TensorFlow session\n      batch_size: The size of batch. Should be specified before training.\n      y_dim: (optional) Dimension of dim for y. [None]\n      z_dim: (optional) Dimension of dim for Z. [100]\n      gf_dim: (optional) Dimension of gen filters in first conv layer. [64]\n      df_dim: (optional) Dimension of discrim filters in first conv layer. [64]\n      gfc_dim: (optional) Dimension of gen units for for fully connected layer. [1024]\n      dfc_dim: (optional) Dimension of discrim units for fully connected layer. [1024]\n      c_dim: (optional) Dimension of image color. For grayscale input, set to 1. [3]\n    \"\"\"\n    self.sess = sess\n    self.crop = crop\n\n    self.batch_size = batch_size\n    self.sample_num = sample_num\n\n    self.input_height = input_height\n    self.input_width = input_width\n    self.output_height = output_height\n    self.output_width = output_width\n\n    self.y_dim = y_dim\n    self.z_dim = z_dim\n\n    self.gf_dim = gf_dim\n    self.df_dim = df_dim\n\n    self.gfc_dim = gfc_dim\n    self.dfc_dim = dfc_dim\n\n    # batch normalization : deals with poor initialization helps gradient flow\n    self.d_bn1 = batch_norm(name='d_bn1')\n    self.d_bn2 = batch_norm(name='d_bn2')\n\n    if not self.y_dim:\n      self.d_bn3 = batch_norm(name='d_bn3')\n\n    self.g_bn0 = batch_norm(name='g_bn0')\n    self.g_bn1 = batch_norm(name='g_bn1')\n    self.g_bn2 = batch_norm(name='g_bn2')\n\n    if not self.y_dim:\n      self.g_bn3 = batch_norm(name='g_bn3')\n\n    self.dataset_name = dataset_name\n    self.input_fname_pattern = input_fname_pattern\n    self.checkpoint_dir = checkpoint_dir\n    self.data_dir = data_dir\n    self.out_dir = out_dir\n    self.max_to_keep = max_to_keep\n\n    if self.dataset_name == 'mnist':\n      self.data_X, self.data_y = self.load_mnist()\n      self.c_dim = self.data_X[0].shape[-1]\n    else:\n      data_path = os.path.join(self.data_dir, self.dataset_name, self.input_fname_pattern)\n      self.data = glob(data_path)\n      if len(self.data) == 0:\n        raise Exception(\"[!] No data found in '\" + data_path + \"'\")\n      np.random.shuffle(self.data)\n      imreadImg = imread(self.data[0])\n      if len(imreadImg.shape) >= 3: #check if image is a non-grayscale image by checking channel number\n        self.c_dim = imread(self.data[0]).shape[-1]\n      else:\n        self.c_dim = 1\n\n      if len(self.data) < self.batch_size:\n        raise Exception(\"[!] Entire dataset size is less than the configured batch_size\")\n    \n    self.grayscale = (self.c_dim == 1)\n\n    self.build_model()\n\n  def build_model(self):\n    if self.y_dim:\n      self.y = tf.placeholder(tf.float32, [self.batch_size, self.y_dim], name='y')\n    else:\n      self.y = None\n\n    if self.crop:\n      image_dims = [self.output_height, self.output_width, self.c_dim]\n    else:\n      image_dims = [self.input_height, self.input_width, self.c_dim]\n\n    self.inputs = tf.placeholder(\n      tf.float32, [self.batch_size] + image_dims, name='real_images')\n\n    inputs = self.inputs\n\n    self.z = tf.placeholder(\n      tf.float32, [None, self.z_dim], name='z')\n    self.z_sum = histogram_summary(\"z\", self.z)\n\n    self.G                  = self.generator(self.z, self.y)\n    self.D, self.D_logits   = self.discriminator(inputs, self.y, reuse=False)\n    self.sampler            = self.sampler(self.z, self.y)\n    self.D_, self.D_logits_ = self.discriminator(self.G, self.y, reuse=True)\n    \n    self.d_sum = histogram_summary(\"d\", self.D)\n    self.d__sum = histogram_summary(\"d_\", self.D_)\n    self.G_sum = image_summary(\"G\", self.G)\n\n    def sigmoid_cross_entropy_with_logits(x, y):\n      try:\n        return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, labels=y)\n      except:\n        return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, targets=y)\n\n    self.d_loss_real = tf.reduce_mean(\n      sigmoid_cross_entropy_with_logits(self.D_logits, tf.ones_like(self.D)))\n    self.d_loss_fake = tf.reduce_mean(\n      sigmoid_cross_entropy_with_logits(self.D_logits_, tf.zeros_like(self.D_)))\n    self.g_loss = tf.reduce_mean(\n      sigmoid_cross_entropy_with_logits(self.D_logits_, tf.ones_like(self.D_)))\n\n    self.d_loss_real_sum = scalar_summary(\"d_loss_real\", self.d_loss_real)\n    self.d_loss_fake_sum = scalar_summary(\"d_loss_fake\", self.d_loss_fake)\n                          \n    self.d_loss = self.d_loss_real + self.d_loss_fake\n\n    self.g_loss_sum = scalar_summary(\"g_loss\", self.g_loss)\n    self.d_loss_sum = scalar_summary(\"d_loss\", self.d_loss)\n\n    t_vars = tf.trainable_variables()\n\n    self.d_vars = [var for var in t_vars if 'd_' in var.name]\n    self.g_vars = [var for var in t_vars if 'g_' in var.name]\n\n    self.saver = tf.train.Saver(max_to_keep=self.max_to_keep)\n\n  def train(self, config):\n    d_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \\\n              .minimize(self.d_loss, var_list=self.d_vars)\n    g_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \\\n              .minimize(self.g_loss, var_list=self.g_vars)\n    try:\n      tf.global_variables_initializer().run()\n    except:\n      tf.initialize_all_variables().run()\n\n    if config.G_img_sum:\n      self.g_sum = merge_summary([self.z_sum, self.d__sum, self.G_sum, self.d_loss_fake_sum, self.g_loss_sum])\n    else:\n      self.g_sum = merge_summary([self.z_sum, self.d__sum, self.d_loss_fake_sum, self.g_loss_sum])\n    self.d_sum = merge_summary(\n        [self.z_sum, self.d_sum, self.d_loss_real_sum, self.d_loss_sum])\n    self.writer = SummaryWriter(os.path.join(self.out_dir, \"logs\"), self.sess.graph)\n\n    sample_z = gen_random(config.z_dist, size=(self.sample_num , self.z_dim))\n    \n    if config.dataset == 'mnist':\n      sample_inputs = self.data_X[0:self.sample_num]\n      sample_labels = self.data_y[0:self.sample_num]\n    else:\n      sample_files = self.data[0:self.sample_num]\n      sample = [\n          get_image(sample_file,\n                    input_height=self.input_height,\n                    input_width=self.input_width,\n                    resize_height=self.output_height,\n                    resize_width=self.output_width,\n                    crop=self.crop,\n                    grayscale=self.grayscale) for sample_file in sample_files]\n      if (self.grayscale):\n        sample_inputs = np.array(sample).astype(np.float32)[:, :, :, None]\n      else:\n        sample_inputs = np.array(sample).astype(np.float32)\n  \n    counter = 1\n    start_time = time.time()\n    could_load, checkpoint_counter = self.load(self.checkpoint_dir)\n    if could_load:\n      counter = checkpoint_counter\n      print(\" [*] Load SUCCESS\")\n    else:\n      print(\" [!] Load failed...\")\n\n    for epoch in xrange(config.epoch):\n      if config.dataset == 'mnist':\n        batch_idxs = min(len(self.data_X), config.train_size) // config.batch_size\n      else:      \n        self.data = glob(os.path.join(\n          config.data_dir, config.dataset, self.input_fname_pattern))\n        np.random.shuffle(self.data)\n        batch_idxs = min(len(self.data), config.train_size) // config.batch_size\n\n      for idx in xrange(0, int(batch_idxs)):\n        if config.dataset == 'mnist':\n          batch_images = self.data_X[idx*config.batch_size:(idx+1)*config.batch_size]\n          batch_labels = self.data_y[idx*config.batch_size:(idx+1)*config.batch_size]\n        else:\n          batch_files = self.data[idx*config.batch_size:(idx+1)*config.batch_size]\n          batch = [\n              get_image(batch_file,\n                        input_height=self.input_height,\n                        input_width=self.input_width,\n                        resize_height=self.output_height,\n                        resize_width=self.output_width,\n                        crop=self.crop,\n                        grayscale=self.grayscale) for batch_file in batch_files]\n          if self.grayscale:\n            batch_images = np.array(batch).astype(np.float32)[:, :, :, None]\n          else:\n            batch_images = np.array(batch).astype(np.float32)\n\n        batch_z = gen_random(config.z_dist, size=[config.batch_size, self.z_dim]) \\\n              .astype(np.float32)\n\n        if config.dataset == 'mnist':\n          # Update D network\n          _, summary_str = self.sess.run([d_optim, self.d_sum],\n            feed_dict={ \n              self.inputs: batch_images,\n              self.z: batch_z,\n              self.y:batch_labels,\n            })\n          self.writer.add_summary(summary_str, counter)\n\n          # Update G network\n          _, summary_str = self.sess.run([g_optim, self.g_sum],\n            feed_dict={\n              self.z: batch_z, \n              self.y:batch_labels,\n            })\n          self.writer.add_summary(summary_str, counter)\n\n          # Run g_optim twice to make sure that d_loss does not go to zero (different from paper)\n          _, summary_str = self.sess.run([g_optim, self.g_sum],\n            feed_dict={ self.z: batch_z, self.y:batch_labels })\n          self.writer.add_summary(summary_str, counter)\n          \n          errD_fake = self.d_loss_fake.eval({\n              self.z: batch_z, \n              self.y:batch_labels\n          })\n          errD_real = self.d_loss_real.eval({\n              self.inputs: batch_images,\n              self.y:batch_labels\n          })\n          errG = self.g_loss.eval({\n              self.z: batch_z,\n              self.y: batch_labels\n          })\n        else:\n          # Update D network\n          _, summary_str = self.sess.run([d_optim, self.d_sum],\n            feed_dict={ self.inputs: batch_images, self.z: batch_z })\n          self.writer.add_summary(summary_str, counter)\n\n          # Update G network\n          _, summary_str = self.sess.run([g_optim, self.g_sum],\n            feed_dict={ self.z: batch_z })\n          self.writer.add_summary(summary_str, counter)\n\n          # Run g_optim twice to make sure that d_loss does not go to zero (different from paper)\n          _, summary_str = self.sess.run([g_optim, self.g_sum],\n            feed_dict={ self.z: batch_z })\n          self.writer.add_summary(summary_str, counter)\n          \n          errD_fake = self.d_loss_fake.eval({ self.z: batch_z })\n          errD_real = self.d_loss_real.eval({ self.inputs: batch_images })\n          errG = self.g_loss.eval({self.z: batch_z})\n\n        print(\"[%8d Epoch:[%2d/%2d] [%4d/%4d] time: %4.4f, d_loss: %.8f, g_loss: %.8f\" \\\n          % (counter, epoch, config.epoch, idx, batch_idxs,\n            time.time() - start_time, errD_fake+errD_real, errG))\n\n        if np.mod(counter, config.sample_freq) == 0:\n          if config.dataset == 'mnist':\n            samples, d_loss, g_loss = self.sess.run(\n              [self.sampler, self.d_loss, self.g_loss],\n              feed_dict={\n                  self.z: sample_z,\n                  self.inputs: sample_inputs,\n                  self.y:sample_labels,\n              }\n            )\n            save_images(samples, image_manifold_size(samples.shape[0]),\n                  './{}/train_{:08d}.png'.format(config.sample_dir, counter))\n            print(\"[Sample] d_loss: %.8f, g_loss: %.8f\" % (d_loss, g_loss)) \n          else:\n            try:\n              samples, d_loss, g_loss = self.sess.run(\n                [self.sampler, self.d_loss, self.g_loss],\n                feed_dict={\n                    self.z: sample_z,\n                    self.inputs: sample_inputs,\n                },\n              )\n              save_images(samples, image_manifold_size(samples.shape[0]),\n                    './{}/train_{:08d}.png'.format(config.sample_dir, counter))\n              print(\"[Sample] d_loss: %.8f, g_loss: %.8f\" % (d_loss, g_loss)) \n            except:\n              print(\"one pic error!...\")\n\n        if np.mod(counter, config.ckpt_freq) == 0:\n          self.save(config.checkpoint_dir, counter)\n        \n        counter += 1\n        \n  def discriminator(self, image, y=None, reuse=False):\n    with tf.variable_scope(\"discriminator\") as scope:\n      if reuse:\n        scope.reuse_variables()\n\n      if not self.y_dim:\n        h0 = lrelu(conv2d(image, self.df_dim, name='d_h0_conv'))\n        h1 = lrelu(self.d_bn1(conv2d(h0, self.df_dim*2, name='d_h1_conv')))\n        h2 = lrelu(self.d_bn2(conv2d(h1, self.df_dim*4, name='d_h2_conv')))\n        h3 = lrelu(self.d_bn3(conv2d(h2, self.df_dim*8, name='d_h3_conv')))\n        h4 = linear(tf.reshape(h3, [self.batch_size, -1]), 1, 'd_h4_lin')\n\n        return tf.nn.sigmoid(h4), h4\n      else:\n        yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])\n        x = conv_cond_concat(image, yb)\n\n        h0 = lrelu(conv2d(x, self.c_dim + self.y_dim, name='d_h0_conv'))\n        h0 = conv_cond_concat(h0, yb)\n\n        h1 = lrelu(self.d_bn1(conv2d(h0, self.df_dim + self.y_dim, name='d_h1_conv')))\n        h1 = tf.reshape(h1, [self.batch_size, -1])      \n        h1 = concat([h1, y], 1)\n        \n        h2 = lrelu(self.d_bn2(linear(h1, self.dfc_dim, 'd_h2_lin')))\n        h2 = concat([h2, y], 1)\n\n        h3 = linear(h2, 1, 'd_h3_lin')\n        \n        return tf.nn.sigmoid(h3), h3\n\n  def generator(self, z, y=None):\n    with tf.variable_scope(\"generator\") as scope:\n      if not self.y_dim:\n        s_h, s_w = self.output_height, self.output_width\n        s_h2, s_w2 = conv_out_size_same(s_h, 2), conv_out_size_same(s_w, 2)\n        s_h4, s_w4 = conv_out_size_same(s_h2, 2), conv_out_size_same(s_w2, 2)\n        s_h8, s_w8 = conv_out_size_same(s_h4, 2), conv_out_size_same(s_w4, 2)\n        s_h16, s_w16 = conv_out_size_same(s_h8, 2), conv_out_size_same(s_w8, 2)\n\n        # project `z` and reshape\n        self.z_, self.h0_w, self.h0_b = linear(\n            z, self.gf_dim*8*s_h16*s_w16, 'g_h0_lin', with_w=True)\n\n        self.h0 = tf.reshape(\n            self.z_, [-1, s_h16, s_w16, self.gf_dim * 8])\n        h0 = tf.nn.relu(self.g_bn0(self.h0))\n\n        self.h1, self.h1_w, self.h1_b = deconv2d(\n            h0, [self.batch_size, s_h8, s_w8, self.gf_dim*4], name='g_h1', with_w=True)\n        h1 = tf.nn.relu(self.g_bn1(self.h1))\n\n        h2, self.h2_w, self.h2_b = deconv2d(\n            h1, [self.batch_size, s_h4, s_w4, self.gf_dim*2], name='g_h2', with_w=True)\n        h2 = tf.nn.relu(self.g_bn2(h2))\n\n        h3, self.h3_w, self.h3_b = deconv2d(\n            h2, [self.batch_size, s_h2, s_w2, self.gf_dim*1], name='g_h3', with_w=True)\n        h3 = tf.nn.relu(self.g_bn3(h3))\n\n        h4, self.h4_w, self.h4_b = deconv2d(\n            h3, [self.batch_size, s_h, s_w, self.c_dim], name='g_h4', with_w=True)\n\n        return tf.nn.tanh(h4)\n      else:\n        s_h, s_w = self.output_height, self.output_width\n        s_h2, s_h4 = int(s_h/2), int(s_h/4)\n        s_w2, s_w4 = int(s_w/2), int(s_w/4)\n\n        # yb = tf.expand_dims(tf.expand_dims(y, 1),2)\n        yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])\n        z = concat([z, y], 1)\n\n        h0 = tf.nn.relu(\n            self.g_bn0(linear(z, self.gfc_dim, 'g_h0_lin')))\n        h0 = concat([h0, y], 1)\n\n        h1 = tf.nn.relu(self.g_bn1(\n            linear(h0, self.gf_dim*2*s_h4*s_w4, 'g_h1_lin')))\n        h1 = tf.reshape(h1, [self.batch_size, s_h4, s_w4, self.gf_dim * 2])\n\n        h1 = conv_cond_concat(h1, yb)\n\n        h2 = tf.nn.relu(self.g_bn2(deconv2d(h1,\n            [self.batch_size, s_h2, s_w2, self.gf_dim * 2], name='g_h2')))\n        h2 = conv_cond_concat(h2, yb)\n\n        return tf.nn.sigmoid(\n            deconv2d(h2, [self.batch_size, s_h, s_w, self.c_dim], name='g_h3'))\n\n  def sampler(self, z, y=None):\n    with tf.variable_scope(\"generator\") as scope:\n      scope.reuse_variables()\n\n      if not self.y_dim:\n        s_h, s_w = self.output_height, self.output_width\n        s_h2, s_w2 = conv_out_size_same(s_h, 2), conv_out_size_same(s_w, 2)\n        s_h4, s_w4 = conv_out_size_same(s_h2, 2), conv_out_size_same(s_w2, 2)\n        s_h8, s_w8 = conv_out_size_same(s_h4, 2), conv_out_size_same(s_w4, 2)\n        s_h16, s_w16 = conv_out_size_same(s_h8, 2), conv_out_size_same(s_w8, 2)\n\n        # project `z` and reshape\n        h0 = tf.reshape(\n            linear(z, self.gf_dim*8*s_h16*s_w16, 'g_h0_lin'),\n            [-1, s_h16, s_w16, self.gf_dim * 8])\n        h0 = tf.nn.relu(self.g_bn0(h0, train=False))\n\n        h1 = deconv2d(h0, [self.batch_size, s_h8, s_w8, self.gf_dim*4], name='g_h1')\n        h1 = tf.nn.relu(self.g_bn1(h1, train=False))\n\n        h2 = deconv2d(h1, [self.batch_size, s_h4, s_w4, self.gf_dim*2], name='g_h2')\n        h2 = tf.nn.relu(self.g_bn2(h2, train=False))\n\n        h3 = deconv2d(h2, [self.batch_size, s_h2, s_w2, self.gf_dim*1], name='g_h3')\n        h3 = tf.nn.relu(self.g_bn3(h3, train=False))\n\n        h4 = deconv2d(h3, [self.batch_size, s_h, s_w, self.c_dim], name='g_h4')\n\n        return tf.nn.tanh(h4)\n      else:\n        s_h, s_w = self.output_height, self.output_width\n        s_h2, s_h4 = int(s_h/2), int(s_h/4)\n        s_w2, s_w4 = int(s_w/2), int(s_w/4)\n\n        # yb = tf.reshape(y, [-1, 1, 1, self.y_dim])\n        yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])\n        z = concat([z, y], 1)\n\n        h0 = tf.nn.relu(self.g_bn0(linear(z, self.gfc_dim, 'g_h0_lin'), train=False))\n        h0 = concat([h0, y], 1)\n\n        h1 = tf.nn.relu(self.g_bn1(\n            linear(h0, self.gf_dim*2*s_h4*s_w4, 'g_h1_lin'), train=False))\n        h1 = tf.reshape(h1, [self.batch_size, s_h4, s_w4, self.gf_dim * 2])\n        h1 = conv_cond_concat(h1, yb)\n\n        h2 = tf.nn.relu(self.g_bn2(\n            deconv2d(h1, [self.batch_size, s_h2, s_w2, self.gf_dim * 2], name='g_h2'), train=False))\n        h2 = conv_cond_concat(h2, yb)\n\n        return tf.nn.sigmoid(deconv2d(h2, [self.batch_size, s_h, s_w, self.c_dim], name='g_h3'))\n\n  def load_mnist(self):\n    data_dir = os.path.join(self.data_dir, self.dataset_name)\n    \n    fd = open(os.path.join(data_dir,'train-images-idx3-ubyte'))\n    loaded = np.fromfile(file=fd,dtype=np.uint8)\n    trX = loaded[16:].reshape((60000,28,28,1)).astype(np.float)\n\n    fd = open(os.path.join(data_dir,'train-labels-idx1-ubyte'))\n    loaded = np.fromfile(file=fd,dtype=np.uint8)\n    trY = loaded[8:].reshape((60000)).astype(np.float)\n\n    fd = open(os.path.join(data_dir,'t10k-images-idx3-ubyte'))\n    loaded = np.fromfile(file=fd,dtype=np.uint8)\n    teX = loaded[16:].reshape((10000,28,28,1)).astype(np.float)\n\n    fd = open(os.path.join(data_dir,'t10k-labels-idx1-ubyte'))\n    loaded = np.fromfile(file=fd,dtype=np.uint8)\n    teY = loaded[8:].reshape((10000)).astype(np.float)\n\n    trY = np.asarray(trY)\n    teY = np.asarray(teY)\n    \n    X = np.concatenate((trX, teX), axis=0)\n    y = np.concatenate((trY, teY), axis=0).astype(np.int)\n    \n    seed = 547\n    np.random.seed(seed)\n    np.random.shuffle(X)\n    np.random.seed(seed)\n    np.random.shuffle(y)\n    \n    y_vec = np.zeros((len(y), self.y_dim), dtype=np.float)\n    for i, label in enumerate(y):\n      y_vec[i,y[i]] = 1.0\n    \n    return X/255.,y_vec\n\n  @property\n  def model_dir(self):\n    return \"{}_{}_{}_{}\".format(\n        self.dataset_name, self.batch_size,\n        self.output_height, self.output_width)\n\n  def save(self, checkpoint_dir, step, filename='model', ckpt=True, frozen=False):\n    # model_name = \"DCGAN.model\"\n    # checkpoint_dir = os.path.join(checkpoint_dir, self.model_dir)\n\n    filename += '.b' + str(self.batch_size)\n    if not os.path.exists(checkpoint_dir):\n      os.makedirs(checkpoint_dir)\n\n    if ckpt:\n      self.saver.save(self.sess,\n              os.path.join(checkpoint_dir, filename),\n              global_step=step)\n\n    if frozen:\n      tf.train.write_graph(\n              tf.graph_util.convert_variables_to_constants(self.sess, self.sess.graph_def, [\"generator_1/Tanh\"]),\n              checkpoint_dir,\n              '{}-{:06d}_frz.pb'.format(filename, step),\n              as_text=False)\n\n  def load(self, checkpoint_dir):\n    #import re\n    print(\" [*] Reading checkpoints...\", checkpoint_dir)\n    # checkpoint_dir = os.path.join(checkpoint_dir, self.model_dir)\n    # print(\"     ->\", checkpoint_dir)\n\n    ckpt = tf.train.get_checkpoint_state(checkpoint_dir)\n    if ckpt and ckpt.model_checkpoint_path:\n      ckpt_name = os.path.basename(ckpt.model_checkpoint_path)\n      self.saver.restore(self.sess, os.path.join(checkpoint_dir, ckpt_name))\n      #counter = int(next(re.finditer(\"(\\d+)(?!.*\\d)\",ckpt_name)).group(0))\n      counter = int(ckpt_name.split('-')[-1])\n      print(\" [*] Success to read {}\".format(ckpt_name))\n      return True, counter\n    else:\n      print(\" [*] Failed to find a checkpoint\")\n      return False, 0\n"
  },
  {
    "path": "ops.py",
    "content": "import math\nimport numpy as np \nimport tensorflow as tf\n\nfrom tensorflow.python.framework import ops\n\nfrom utils import *\n\ntry:\n  image_summary = tf.image_summary\n  scalar_summary = tf.scalar_summary\n  histogram_summary = tf.histogram_summary\n  merge_summary = tf.merge_summary\n  SummaryWriter = tf.train.SummaryWriter\nexcept:\n  image_summary = tf.summary.image\n  scalar_summary = tf.summary.scalar\n  histogram_summary = tf.summary.histogram\n  merge_summary = tf.summary.merge\n  SummaryWriter = tf.summary.FileWriter\n\nif \"concat_v2\" in dir(tf):\n  def concat(tensors, axis, *args, **kwargs):\n    return tf.concat_v2(tensors, axis, *args, **kwargs)\nelse:\n  def concat(tensors, axis, *args, **kwargs):\n    return tf.concat(tensors, axis, *args, **kwargs)\n\nclass batch_norm(object):\n  def __init__(self, epsilon=1e-5, momentum = 0.9, name=\"batch_norm\"):\n    with tf.variable_scope(name):\n      self.epsilon  = epsilon\n      self.momentum = momentum\n      self.name = name\n\n  def __call__(self, x, train=True):\n    return tf.contrib.layers.batch_norm(x,\n                      decay=self.momentum, \n                      updates_collections=None,\n                      epsilon=self.epsilon,\n                      scale=True,\n                      is_training=train,\n                      scope=self.name)\n\ndef conv_cond_concat(x, y):\n  \"\"\"Concatenate conditioning vector on feature map axis.\"\"\"\n  x_shapes = x.get_shape()\n  y_shapes = y.get_shape()\n  return concat([\n    x, y*tf.ones([x_shapes[0], x_shapes[1], x_shapes[2], y_shapes[3]])], 3)\n\ndef conv2d(input_, output_dim, \n       k_h=5, k_w=5, d_h=2, d_w=2, stddev=0.02,\n       name=\"conv2d\"):\n  with tf.variable_scope(name):\n    w = tf.get_variable('w', [k_h, k_w, input_.get_shape()[-1], output_dim],\n              initializer=tf.truncated_normal_initializer(stddev=stddev))\n    conv = tf.nn.conv2d(input_, w, strides=[1, d_h, d_w, 1], padding='SAME')\n\n    biases = tf.get_variable('biases', [output_dim], initializer=tf.constant_initializer(0.0))\n    conv = tf.reshape(tf.nn.bias_add(conv, biases), conv.get_shape())\n\n    return conv\n\ndef deconv2d(input_, output_shape,\n       k_h=5, k_w=5, d_h=2, d_w=2, stddev=0.02,\n       name=\"deconv2d\", with_w=False):\n  with tf.variable_scope(name):\n    # filter : [height, width, output_channels, in_channels]\n    w = tf.get_variable('w', [k_h, k_w, output_shape[-1], input_.get_shape()[-1]],\n              initializer=tf.random_normal_initializer(stddev=stddev))\n    \n    try:\n      deconv = tf.nn.conv2d_transpose(input_, w, output_shape=output_shape,\n                strides=[1, d_h, d_w, 1])\n\n    # Support for verisons of TensorFlow before 0.7.0\n    except AttributeError:\n      deconv = tf.nn.deconv2d(input_, w, output_shape=output_shape,\n                strides=[1, d_h, d_w, 1])\n\n    biases = tf.get_variable('biases', [output_shape[-1]], initializer=tf.constant_initializer(0.0))\n    deconv = tf.reshape(tf.nn.bias_add(deconv, biases), deconv.get_shape())\n\n    if with_w:\n      return deconv, w, biases\n    else:\n      return deconv\n     \ndef lrelu(x, leak=0.2, name=\"lrelu\"):\n  return tf.maximum(x, leak*x)\n\ndef linear(input_, output_size, scope=None, stddev=0.02, bias_start=0.0, with_w=False):\n  shape = input_.get_shape().as_list()\n\n  with tf.variable_scope(scope or \"Linear\"):\n    try:\n      matrix = tf.get_variable(\"Matrix\", [shape[1], output_size], tf.float32,\n                 tf.random_normal_initializer(stddev=stddev))\n    except ValueError as err:\n        msg = \"NOTE: Usually, this is due to an issue with the image dimensions.  Did you correctly set '--crop' or '--input_height' or '--output_height'?\"\n        err.args = err.args + (msg,)\n        raise\n    bias = tf.get_variable(\"bias\", [output_size],\n      initializer=tf.constant_initializer(bias_start))\n    if with_w:\n      return tf.matmul(input_, matrix) + bias, matrix, bias\n    else:\n      return tf.matmul(input_, matrix) + bias\n"
  },
  {
    "path": "utils.py",
    "content": "\"\"\"\nSome codes from https://github.com/Newmu/dcgan_code\n\"\"\"\nfrom __future__ import division\nimport math\nimport json\nimport random\nimport pprint\nimport scipy.misc\nimport cv2\nimport numpy as np\nimport os\nimport time\nimport datetime\nfrom time import gmtime, strftime\nfrom six.moves import xrange\nfrom PIL import Image\n\nimport tensorflow as tf\nimport tensorflow.contrib.slim as slim\n\npp = pprint.PrettyPrinter()\n\nget_stddev = lambda x, k_h, k_w: 1/math.sqrt(k_w*k_h*x.get_shape()[-1])\n\n\ndef expand_path(path):\n  return os.path.expanduser(os.path.expandvars(path))\n\ndef timestamp(s='%Y%m%d.%H%M%S', ts=None):\n  if not ts: ts = time.time()\n  st = datetime.datetime.fromtimestamp(ts).strftime(s)\n  return st\n  \ndef show_all_variables():\n  model_vars = tf.trainable_variables()\n  slim.model_analyzer.analyze_vars(model_vars, print_info=True)\n\ndef get_image(image_path, input_height, input_width,\n              resize_height=64, resize_width=64,\n              crop=True, grayscale=False):\n  image = imread(image_path, grayscale)\n  return transform(image, input_height, input_width,\n                   resize_height, resize_width, crop)\n\ndef save_images(images, size, image_path):\n  return imsave(inverse_transform(images), size, image_path)\n\ndef imread(path, grayscale = False):\n  if (grayscale):\n    return scipy.misc.imread(path, flatten = True).astype(np.float)\n  else:\n    # Reference: https://github.com/carpedm20/DCGAN-tensorflow/issues/162#issuecomment-315519747\n    img_bgr = cv2.imread(path)\n    # Reference: https://stackoverflow.com/a/15074748/\n    img_rgb = img_bgr[..., ::-1]\n    return img_rgb.astype(np.float)\n\ndef merge_images(images, size):\n  return inverse_transform(images)\n\ndef merge(images, size):\n  h, w = images.shape[1], images.shape[2]\n  if (images.shape[3] in (3,4)):\n    c = images.shape[3]\n    img = np.zeros((h * size[0], w * size[1], c))\n    for idx, image in enumerate(images):\n      i = idx % size[1]\n      j = idx // size[1]\n      img[j * h:j * h + h, i * w:i * w + w, :] = image\n    return img\n  elif images.shape[3]==1:\n    img = np.zeros((h * size[0], w * size[1]))\n    for idx, image in enumerate(images):\n      i = idx % size[1]\n      j = idx // size[1]\n      img[j * h:j * h + h, i * w:i * w + w] = image[:,:,0]\n    return img\n  else:\n    raise ValueError('in merge(images,size) images parameter '\n                     'must have dimensions: HxW or HxWx3 or HxWx4')\n\ndef imsave(images, size, path):\n  image = np.squeeze(merge(images, size))\n  return scipy.misc.imsave(path, image)\n\ndef center_crop(x, crop_h, crop_w,\n                resize_h=64, resize_w=64):\n  if crop_w is None:\n    crop_w = crop_h\n  h, w = x.shape[:2]\n  j = int(round((h - crop_h)/2.))\n  i = int(round((w - crop_w)/2.))\n  im = Image.fromarray(x[j:j+crop_h, i:i+crop_w])\n  return np.array(im.resize([resize_h, resize_w]), PIL.Image.BILINEAR)\n\ndef transform(image, input_height, input_width, \n              resize_height=64, resize_width=64, crop=True):\n  if crop:\n    cropped_image = center_crop(\n      image, input_height, input_width, \n      resize_height, resize_width)\n  else:\n    im = Image.fromarray(image[j:j+crop_h, i:i+crop_w])\n  return np.array(im.resize([resize_h, resize_w]), PIL.Image.BILINEAR)/127.5 - 1.\n\ndef inverse_transform(images):\n  return (images+1.)/2.\n\ndef to_json(output_path, *layers):\n  with open(output_path, \"w\") as layer_f:\n    lines = \"\"\n    for w, b, bn in layers:\n      layer_idx = w.name.split('/')[0].split('h')[1]\n\n      B = b.eval()\n\n      if \"lin/\" in w.name:\n        W = w.eval()\n        depth = W.shape[1]\n      else:\n        W = np.rollaxis(w.eval(), 2, 0)\n        depth = W.shape[0]\n\n      biases = {\"sy\": 1, \"sx\": 1, \"depth\": depth, \"w\": ['%.2f' % elem for elem in list(B)]}\n      if bn != None:\n        gamma = bn.gamma.eval()\n        beta = bn.beta.eval()\n\n        gamma = {\"sy\": 1, \"sx\": 1, \"depth\": depth, \"w\": ['%.2f' % elem for elem in list(gamma)]}\n        beta = {\"sy\": 1, \"sx\": 1, \"depth\": depth, \"w\": ['%.2f' % elem for elem in list(beta)]}\n      else:\n        gamma = {\"sy\": 1, \"sx\": 1, \"depth\": 0, \"w\": []}\n        beta = {\"sy\": 1, \"sx\": 1, \"depth\": 0, \"w\": []}\n\n      if \"lin/\" in w.name:\n        fs = []\n        for w in W.T:\n          fs.append({\"sy\": 1, \"sx\": 1, \"depth\": W.shape[0], \"w\": ['%.2f' % elem for elem in list(w)]})\n\n        lines += \"\"\"\n          var layer_%s = {\n            \"layer_type\": \"fc\", \n            \"sy\": 1, \"sx\": 1, \n            \"out_sx\": 1, \"out_sy\": 1,\n            \"stride\": 1, \"pad\": 0,\n            \"out_depth\": %s, \"in_depth\": %s,\n            \"biases\": %s,\n            \"gamma\": %s,\n            \"beta\": %s,\n            \"filters\": %s\n          };\"\"\" % (layer_idx.split('_')[0], W.shape[1], W.shape[0], biases, gamma, beta, fs)\n      else:\n        fs = []\n        for w_ in W:\n          fs.append({\"sy\": 5, \"sx\": 5, \"depth\": W.shape[3], \"w\": ['%.2f' % elem for elem in list(w_.flatten())]})\n\n        lines += \"\"\"\n          var layer_%s = {\n            \"layer_type\": \"deconv\", \n            \"sy\": 5, \"sx\": 5,\n            \"out_sx\": %s, \"out_sy\": %s,\n            \"stride\": 2, \"pad\": 1,\n            \"out_depth\": %s, \"in_depth\": %s,\n            \"biases\": %s,\n            \"gamma\": %s,\n            \"beta\": %s,\n            \"filters\": %s\n          };\"\"\" % (layer_idx, 2**(int(layer_idx)+2), 2**(int(layer_idx)+2),\n               W.shape[0], W.shape[3], biases, gamma, beta, fs)\n    layer_f.write(\" \".join(lines.replace(\"'\",\"\").split()))\n\ndef make_gif(images, fname, duration=2, true_image=False):\n  import moviepy.editor as mpy\n\n  def make_frame(t):\n    try:\n      x = images[int(len(images)/duration*t)]\n    except:\n      x = images[-1]\n\n    if true_image:\n      return x.astype(np.uint8)\n    else:\n      return ((x+1)/2*255).astype(np.uint8)\n\n  clip = mpy.VideoClip(make_frame, duration=duration)\n  clip.write_gif(fname, fps = len(images) / duration)\n\ndef visualize(sess, dcgan, config, option, sample_dir='samples'):\n  image_frame_dim = int(math.ceil(config.batch_size**.5))\n  if option == 0:\n    z_sample = np.random.uniform(-0.5, 0.5, size=(config.batch_size, dcgan.z_dim))\n    samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})\n    save_images(samples, [image_frame_dim, image_frame_dim], os.path.join(sample_dir, 'test_%s.png' % strftime(\"%Y%m%d%H%M%S\", gmtime() )))\n  elif option == 1:\n    values = np.arange(0, 1, 1./config.batch_size)\n    for idx in xrange(dcgan.z_dim):\n      print(\" [*] %d\" % idx)\n      z_sample = np.random.uniform(-1, 1, size=(config.batch_size , dcgan.z_dim))\n      for kdx, z in enumerate(z_sample):\n        z[idx] = values[kdx]\n\n      if config.dataset == \"mnist\":\n        y = np.random.choice(10, config.batch_size)\n        y_one_hot = np.zeros((config.batch_size, 10))\n        y_one_hot[np.arange(config.batch_size), y] = 1\n\n        samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample, dcgan.y: y_one_hot})\n      else:\n        samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})\n\n      save_images(samples, [image_frame_dim, image_frame_dim], os.path.join(sample_dir, 'test_arange_%s.png' % (idx)))\n  elif option == 2:\n    values = np.arange(0, 1, 1./config.batch_size)\n    for idx in [random.randint(0, dcgan.z_dim - 1) for _ in xrange(dcgan.z_dim)]:\n      print(\" [*] %d\" % idx)\n      z = np.random.uniform(-0.2, 0.2, size=(dcgan.z_dim))\n      z_sample = np.tile(z, (config.batch_size, 1))\n      #z_sample = np.zeros([config.batch_size, dcgan.z_dim])\n      for kdx, z in enumerate(z_sample):\n        z[idx] = values[kdx]\n\n      if config.dataset == \"mnist\":\n        y = np.random.choice(10, config.batch_size)\n        y_one_hot = np.zeros((config.batch_size, 10))\n        y_one_hot[np.arange(config.batch_size), y] = 1\n\n        samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample, dcgan.y: y_one_hot})\n      else:\n        samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})\n\n      try:\n        make_gif(samples, './samples/test_gif_%s.gif' % (idx))\n      except:\n        save_images(samples, [image_frame_dim, image_frame_dim], os.path.join(sample_dir, 'test_%s.png' % strftime(\"%Y%m%d%H%M%S\", gmtime() )))\n  elif option == 3:\n    values = np.arange(0, 1, 1./config.batch_size)\n    for idx in xrange(dcgan.z_dim):\n      print(\" [*] %d\" % idx)\n      z_sample = np.zeros([config.batch_size, dcgan.z_dim])\n      for kdx, z in enumerate(z_sample):\n        z[idx] = values[kdx]\n\n      samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})\n      make_gif(samples, os.path.join(sample_dir, 'test_gif_%s.gif' % (idx)))\n  elif option == 4:\n    image_set = []\n    values = np.arange(0, 1, 1./config.batch_size)\n\n    for idx in xrange(dcgan.z_dim):\n      print(\" [*] %d\" % idx)\n      z_sample = np.zeros([config.batch_size, dcgan.z_dim])\n      for kdx, z in enumerate(z_sample): z[idx] = values[kdx]\n\n      image_set.append(sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample}))\n      make_gif(image_set[-1], os.path.join(sample_dir, 'test_gif_%s.gif' % (idx)))\n\n    new_image_set = [merge(np.array([images[idx] for images in image_set]), [10, 10]) \\\n        for idx in range(64) + range(63, -1, -1)]\n    make_gif(new_image_set, './samples/test_gif_merged.gif', duration=8)\n\n\ndef image_manifold_size(num_images):\n  manifold_h = int(np.floor(np.sqrt(num_images)))\n  manifold_w = int(np.ceil(np.sqrt(num_images)))\n  assert manifold_h * manifold_w == num_images\n  return manifold_h, manifold_w\n"
  },
  {
    "path": "web/app.py",
    "content": "from flask import Flask\nfrom flask import render_template\n\napp = Flask(__name__, template_folder=\"./\", static_folder='./', static_url_path='')\n\n@app.route('/')\ndef index():\n  return render_template('index.html')\n\nif __name__ == '__main__':\n  app.debug=True\n  app.run(host='0.0.0.0')\n"
  },
  {
    "path": "web/css/fakeLoader.css",
    "content": "/**********************\n *CSS Animations by:\n *http://codepen.io/vivinantony\n***********************/\n.spinner1 {\n  width: 40px;\n  height: 40px;\n  position: relative;\n}\n\n\n.double-bounce1, .double-bounce2 {\n  width: 100%;\n  height: 100%;\n  border-radius: 50%;\n  background-color: #fff;\n  opacity: 0.6;\n  position: absolute;\n  top: 0;\n  left: 0;\n  \n  -webkit-animation: bounce 2.0s infinite ease-in-out;\n  animation: bounce 2.0s infinite ease-in-out;\n}\n\n.double-bounce2 {\n  -webkit-animation-delay: -1.0s;\n  animation-delay: -1.0s;\n}\n\n@-webkit-keyframes bounce {\n  0%, 100% { -webkit-transform: scale(0.0) }\n  50% { -webkit-transform: scale(1.0) }\n}\n\n@keyframes bounce {\n  0%, 100% { \n    transform: scale(0.0);\n    -webkit-transform: scale(0.0);\n  } 50% { \n    transform: scale(1.0);\n    -webkit-transform: scale(1.0);\n  }\n}\n\n.spinner2 {\n  width: 40px;\n  height: 40px;\n  position: relative;\n}\n\n\n.container1 > div, .container2 > div, .container3 > div {\n  width: 6px;\n  height: 6px;\n  background-color: #fff;\n\n  border-radius: 100%;\n  position: absolute;\n  -webkit-animation: bouncedelay 1.2s infinite ease-in-out;\n  animation: bouncedelay 1.2s infinite ease-in-out;\n  /* Prevent first frame from flickering when animation starts */\n  -webkit-animation-fill-mode: both;\n  animation-fill-mode: both;\n}\n\n.spinner2 .spinner-container {\n  position: absolute;\n  width: 100%;\n  height: 100%;\n}\n\n.container2 {\n  -webkit-transform: rotateZ(45deg);\n  transform: rotateZ(45deg);\n}\n\n.container3 {\n  -webkit-transform: rotateZ(90deg);\n  transform: rotateZ(90deg);\n}\n\n.circle1 { top: 0; left: 0; }\n.circle2 { top: 0; right: 0; }\n.circle3 { right: 0; bottom: 0; }\n.circle4 { left: 0; bottom: 0; }\n\n.container2 .circle1 {\n  -webkit-animation-delay: -1.1s;\n  animation-delay: -1.1s;\n}\n\n.container3 .circle1 {\n  -webkit-animation-delay: -1.0s;\n  animation-delay: -1.0s;\n}\n\n.container1 .circle2 {\n  -webkit-animation-delay: -0.9s;\n  animation-delay: -0.9s;\n}\n\n.container2 .circle2 {\n  -webkit-animation-delay: -0.8s;\n  animation-delay: -0.8s;\n}\n\n.container3 .circle2 {\n  -webkit-animation-delay: -0.7s;\n  animation-delay: -0.7s;\n}\n\n.container1 .circle3 {\n  -webkit-animation-delay: -0.6s;\n  animation-delay: -0.6s;\n}\n\n.container2 .circle3 {\n  -webkit-animation-delay: -0.5s;\n  animation-delay: -0.5s;\n}\n\n.container3 .circle3 {\n  -webkit-animation-delay: -0.4s;\n  animation-delay: -0.4s;\n}\n\n.container1 .circle4 {\n  -webkit-animation-delay: -0.3s;\n  animation-delay: -0.3s;\n}\n\n.container2 .circle4 {\n  -webkit-animation-delay: -0.2s;\n  animation-delay: -0.2s;\n}\n\n.container3 .circle4 {\n  -webkit-animation-delay: -0.1s;\n  animation-delay: -0.1s;\n}\n\n@-webkit-keyframes bouncedelay {\n  0%, 80%, 100% { -webkit-transform: scale(0.0) }\n  40% { -webkit-transform: scale(1.0) }\n}\n\n@keyframes bouncedelay {\n  0%, 80%, 100% { \n    transform: scale(0.0);\n    -webkit-transform: scale(0.0);\n  } 40% { \n    transform: scale(1.0);\n    -webkit-transform: scale(1.0);\n  }\n}\n\n.spinner3 {\n  width: 40px;\n  height: 40px;\n  position: relative;  \n  -webkit-animation: rotate 2.0s infinite linear;\n  animation: rotate 2.0s infinite linear;\n}\n\n.dot1, .dot2 {\n  width: 60%;\n  height: 60%;\n  display: inline-block;\n  position: absolute;\n  top: 0;\n  background-color: #fff;\n  border-radius: 100%;\n  \n  -webkit-animation: bounce 2.0s infinite ease-in-out;\n  animation: bounce 2.0s infinite ease-in-out;\n}\n\n.dot2 {\n  top: auto;\n  bottom: 0px;\n  -webkit-animation-delay: -1.0s;\n  animation-delay: -1.0s;\n}\n\n@-webkit-keyframes rotate { 100% { -webkit-transform: rotate(360deg) }}\n@keyframes rotate { 100% { transform: rotate(360deg); -webkit-transform: rotate(360deg) }}\n\n@-webkit-keyframes bounce {\n  0%, 100% { -webkit-transform: scale(0.0) }\n  50% { -webkit-transform: scale(1.0) }\n}\n\n@keyframes bounce {\n  0%, 100% { \n    transform: scale(0.0);\n    -webkit-transform: scale(0.0);\n  } 50% { \n    transform: scale(1.0);\n    -webkit-transform: scale(1.0);\n  }\n}\n\n.spinner4 {\n  width: 30px;\n  height: 30px;\n  background-color: #fff;\n  -webkit-animation: rotateplane 1.2s infinite ease-in-out;\n  animation: rotateplane 1.2s infinite ease-in-out;\n}\n\n@-webkit-keyframes rotateplane {\n  0% { -webkit-transform: perspective(120px) }\n  50% { -webkit-transform: perspective(120px) rotateY(180deg) }\n  100% { -webkit-transform: perspective(120px) rotateY(180deg)  rotateX(180deg) }\n}\n\n@keyframes rotateplane {\n  0% { \n    transform: perspective(120px) rotateX(0deg) rotateY(0deg);\n    -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg) \n  } 50% { \n    transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);\n    -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) \n  } 100% { \n    transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);\n    -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);\n  }\n}\n\n\n\n\n\n\n@charset 'UTF-8';\n/* Slider */\n.slick-loading .slick-list\n{\n    background: #fff url('./ajax-loader.gif') center center no-repeat;\n}\n\n/* Icons */\n@font-face\n{\n    font-family: 'slick';\n    font-weight: normal;\n    font-style: normal;\n\n    src: url('../fonts/slick.eot');\n    src: url('../fonts/slick.eot?#iefix') format('embedded-opentype'), url('../fonts/slick.woff') format('woff'), url('../fonts/slick.ttf') format('truetype'), url('../fonts/slick.svg#slick') format('svg');\n}\n/* Arrows */\n.slick-prev,\n.slick-next\n{\n    font-size: 0;\n    line-height: 0;\n\n    position: absolute;\n    top: 50%;\n\n    display: block;\n\n    width: 20px;\n    height: 20px;\n    margin-top: -10px;\n    padding: 0;\n\n    cursor: pointer;\n\n    color: transparent;\n    border: none;\n    outline: none;\n    background: transparent;\n}\n.slick-prev:hover,\n.slick-prev:focus,\n.slick-next:hover,\n.slick-next:focus\n{\n    color: transparent;\n    outline: none;\n    background: transparent;\n}\n.slick-prev:hover:before,\n.slick-prev:focus:before,\n.slick-next:hover:before,\n.slick-next:focus:before\n{\n    opacity: 1;\n}\n.slick-prev.slick-disabled:before,\n.slick-next.slick-disabled:before\n{\n    opacity: .25;\n}\n\n.slick-prev:before,\n.slick-next:before\n{\n    font-family: 'slick';\n    font-size: 20px;\n    line-height: 1;\n\n    opacity: .75;\n    color: white;\n\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n}\n\n.slick-prev\n{\n    left: -25px;\n}\n[dir='rtl'] .slick-prev\n{\n    right: -25px;\n    left: auto;\n}\n.slick-prev:before\n{\n    content: '←';\n}\n[dir='rtl'] .slick-prev:before\n{\n    content: '→';\n}\n\n.slick-next\n{\n    right: -25px;\n}\n[dir='rtl'] .slick-next\n{\n    right: auto;\n    left: -25px;\n}\n.slick-next:before\n{\n    content: '→';\n}\n[dir='rtl'] .slick-next:before\n{\n    content: '←';\n}\n\n/* Dots */\n.slick-slider\n{\n    margin-bottom: 30px;\n}\n\n.slick-dots\n{\n    position: absolute;\n    bottom: -45px;\n\n    display: block;\n\n    width: 100%;\n    padding: 0;\n\n    list-style: none;\n\n    text-align: center;\n}\n.slick-dots li\n{\n    position: relative;\n\n    display: inline-block;\n\n    width: 20px;\n    height: 20px;\n    margin: 0 5px;\n    padding: 0;\n\n    cursor: pointer;\n}\n.slick-dots li button\n{\n    font-size: 0;\n    line-height: 0;\n\n    display: block;\n\n    width: 20px;\n    height: 20px;\n    padding: 5px;\n\n    cursor: pointer;\n\n    color: transparent;\n    border: 0;\n    outline: none;\n    background: transparent;\n}\n.slick-dots li button:hover,\n.slick-dots li button:focus\n{\n    outline: none;\n}\n.slick-dots li button:hover:before,\n.slick-dots li button:focus:before\n{\n    opacity: 1;\n}\n.slick-dots li button:before\n{\n    font-family: 'slick';\n    font-size: 6px;\n    line-height: 20px;\n\n    position: absolute;\n    top: 0;\n    left: 0;\n\n    width: 20px;\n    height: 20px;\n\n    content: '•';\n    text-align: center;\n\n    opacity: .25;\n    color: white;\n\n    -webkit-font-smoothing: antialiased;\n    -moz-osx-font-smoothing: grayscale;\n}\n.slick-dots li.slick-active button:before\n{\n    opacity: .75;\n    color: white;\n}"
  },
  {
    "path": "web/css/main.css",
    "content": "canvas {\n    background-color: white;\n}\n\n#colors {\n    overflow: hidden;\n    width: 90px;\n    margin-left: 30px;\n    float: left;\n}\n\n#colors .color {\n    float: left;\n    width: 40px;\n    height: 40px;\n}\n\n#colors .color div {\n    width: 100%;\n    height: 100%;\n  cursor: pointer;\n}\n\n#colors .color #color_1::before { background-color: #000000; }\n#colors .color #color_1::after { background-color: #000000; }\n#colors .color #color_2::before { background-color: #444444; }\n#colors .color #color_2::after { background-color: #303030; }\n#colors .color #color_3::before { background-color: #000088; }\n#colors .color #color_3::after { background-color: #00005f; }\n#colors .color #color_4::before { background-color: #1111ff; }\n#colors .color #color_4::after { background-color: #0000be; }\n#colors .color #color_5::before { background-color: #008800; }\n#colors .color #color_5::after { background-color: #005f00; }\n#colors .color #color_6::before { background-color: #11ff11; }\n#colors .color #color_6::after { background-color: #00be00; }\n#colors .color #color_7::before { background-color: #008888; }\n#colors .color #color_7::after { background-color: #005f5f; }\n#colors .color #color_8::before { background-color: #11ffff; }\n#colors .color #color_8::after { background-color: #00bebe; }\n#colors .color #color_9::before { background-color: #880000; }\n#colors .color #color_9::after { background-color: #5f0000; }\n#colors .color #color_10::before { background-color: #ff1111; }\n#colors .color #color_10::after { background-color: #be0000; }\n#colors .color #color_11::before { background-color: #880088; }\n#colors .color #color_11::after { background-color: #5f005f; }\n#colors .color #color_12::before { background-color: #ff11ff; }\n#colors .color #color_12::after { background-color: #be00be; }\n#colors .color #color_13::before { background-color: #884400; }\n#colors .color #color_13::after { background-color: #5f3000; }\n#colors .color #color_14::before { background-color: #ffff11; }\n#colors .color #color_14::after { background-color: #bebe00; }\n#colors .color #color_15::before { background-color: #888888; }\n#colors .color #color_15::after { background-color: #5f5f5f; }\n#colors .color #color_16::before { background-color: #cccccc; }\n#colors .color #color_16::after { background-color: #8f8f8f; }\n\n#colors .color .isometric::after {\n    width: 40px;\n    height: 10px;\n}\n#colors .color .isometric::before {\n    width: 10px;\n    height: 40px;\n}\n\n\n#colors .color.active div {\n    top: 10px;\n    left: 10px;\n}\n\n/* CANVAS */\n\n#canvas {\n    float: left;\n    cursor: crosshair;\n    width: 320px;\n    height: 320px;\n    margin: 0 auto;\n}\n\n#canvas.isometric::after {\n    width: 320px;\n    height: 10px;\n}\n#canvas.isometric::before {\n    width: 10px;\n    height: 320px;\n}\n\n/*!\n * Start Bootstrap - Grayscale Bootstrap Theme (http://startbootstrap.com)\n * Code licensed under the Apache License v2.0.\n * For details, see http://www.apache.org/licenses/LICENSE-2.0.\n */\n\nbody {\n    width: 100%;\n    height: 100%;\n    font-family: Lora,\"Helvetica Neue\",Helvetica,Arial,sans-serif;\n    color: #fff;\n    background-color: #000;\n}\n\nhtml {\n    width: 100%;\n    height: 100%;\n}\n\nh1,\nh2,\nh3,\nh4,\nh5,\nh6 {\n    margin: 0 0 35px;\n    text-transform: uppercase;\n    font-family: Montserrat,\"Helvetica Neue\",Helvetica,Arial,sans-serif;\n    font-weight: 700;\n    letter-spacing: 1px;\n}\n\np {\n    margin: 0 0 25px;\n    font-size: 18px;\n    line-height: 1.5;\n}\n\n@media(min-width:768px) {\n    p {\n        margin: 0 0 35px;\n        font-size: 20px;\n        line-height: 1.6;\n    }\n}\n\nb {\n    color: #3A8BB2;\n}\n\na {\n    color: #42dca3;\n    -webkit-transition: all .2s ease-in-out;\n    -moz-transition: all .2s ease-in-out;\n    transition: all .2s ease-in-out;\n}\n\na:hover,\na:focus {\n    text-decoration: none;\n    color: #1d9b6c;\n}\n\n.light {\n    font-weight: 400;\n}\n\n.navbar-custom {\n    margin-bottom: 0;\n    border-bottom: 1px solid rgba(255,255,255,.3);\n    text-transform: uppercase;\n    font-family: Montserrat,\"Helvetica Neue\",Helvetica,Arial,sans-serif;\n    background-color: #000;\n}\n\n.navbar-custom .navbar-brand {\n    font-weight: 700;\n}\n\n.navbar-custom .navbar-brand:focus {\n    outline: 0;\n}\n\n.navbar-custom .navbar-brand .navbar-toggle {\n    padding: 4px 6px;\n    font-size: 16px;\n    color: #fff;\n}\n\n.navbar-custom .navbar-brand .navbar-toggle:focus,\n.navbar-custom .navbar-brand .navbar-toggle:active {\n    outline: 0;\n}\n\n.navbar-custom a {\n    color: #fff;\n}\n\n.navbar-custom .nav li a {\n    -webkit-transition: background .3s ease-in-out;\n    -moz-transition: background .3s ease-in-out;\n    transition: background .3s ease-in-out;\n}\n\n.navbar-custom .nav li a:hover {\n    outline: 0;\n    color: rgba(255,255,255,.8);\n    background-color: transparent;\n}\n\n.navbar-custom .nav li a:focus,\n.navbar-custom .nav li a:active {\n    outline: 0;\n    background-color: transparent;\n}\n\n.navbar-custom .nav li.active {\n    outline: 0;\n}\n\n.navbar-custom .nav li.active a {\n    background-color: rgba(255,255,255,.3);\n}\n\n.navbar-custom .nav li.active a:hover {\n    color: #fff;\n}\n\n@media(min-width:768px) {\n    .navbar-custom {\n        padding: 20px 0;\n        border-bottom: 0;\n        letter-spacing: 1px;\n        background: 0 0;\n        -webkit-transition: background .5s ease-in-out,padding .5s ease-in-out;\n        -moz-transition: background .5s ease-in-out,padding .5s ease-in-out;\n        transition: background .5s ease-in-out,padding .5s ease-in-out;\n    }\n\n    .navbar-custom.top-nav-collapse {\n        padding: 0;\n        border-bottom: 1px solid rgba(255,255,255,.3);\n        background: #000;\n    }\n}\n\n#background {\n    position: absolute;\n    top: 0;\n    left: 0;\n    min-width: 100%;\n    min-height: 100%;\n    width: auto;\n    height: auto;\n}\n\n.intro {\n    overflow: hidden;\n    position: relative;\n    display: table;\n    width: 100%;\n    height: auto;\n    min-height: 100%;\n    padding: 100px 0;\n    text-align: center;\n    color: #fff;\n    background-color: #000;\n    -webkit-background-size: cover;\n    -moz-background-size: cover;\n    background-size: cover;\n    -o-background-size: cover;\n}\n\n.intro .intro-body {\n    display: table-cell;\n    vertical-align: middle;\n}\n\n.intro .intro-body .brand-heading {\n    font-size: 40px;\n}\n\n.intro .intro-body .intro-text {\n    font-size: 18px;\n}\n\n@media(min-width:768px) {\n    .intro {\n        height: 100%;\n        padding: 0;\n    }\n\n    .intro .intro-body .brand-heading {\n        font-size: 100px;\n    }\n\n    .intro .intro-body .intro-text {\n        font-size: 26px;\n    }\n}\n\n.btn-circle {\n    width: 70px;\n    height: 70px;\n    margin-top: 15px;\n    padding: 7px 16px;\n    border: 2px solid #fff;\n    border-radius: 100%!important;\n    font-size: 40px;\n    color: #fff;\n    background: 0 0;\n    -webkit-transition: background .3s ease-in-out;\n    -moz-transition: background .3s ease-in-out;\n    transition: background .3s ease-in-out;\n}\n\n.btn-circle:hover,\n.btn-circle:focus {\n    outline: 0;\n    color: #fff;\n    background: rgba(255,255,255,.1);\n}\n\n.btn-circle i.animated {\n    -webkit-transition-property: -webkit-transform;\n    -webkit-transition-duration: 1s;\n    -moz-transition-property: -moz-transform;\n    -moz-transition-duration: 1s;\n}\n\n.btn-circle:hover i.animated {\n    -webkit-animation-name: pulse;\n    -moz-animation-name: pulse;\n    -webkit-animation-duration: 1.5s;\n    -moz-animation-duration: 1.5s;\n    -webkit-animation-iteration-count: infinite;\n    -moz-animation-iteration-count: infinite;\n    -webkit-animation-timing-function: linear;\n    -moz-animation-timing-function: linear;\n}\n\n@-webkit-keyframes pulse {    \n    0% {\n        -webkit-transform: scale(1);\n        transform: scale(1);\n    }\n\n    50% {\n        -webkit-transform: scale(1.2);\n        transform: scale(1.2);\n    }\n\n    100% {\n        -webkit-transform: scale(1);\n        transform: scale(1);\n    }\n}\n\n@-moz-keyframes pulse {    \n    0% {\n        -moz-transform: scale(1);\n        transform: scale(1);\n    }\n\n    50% {\n        -moz-transform: scale(1.2);\n        transform: scale(1.2);\n    }\n\n    100% {\n        -moz-transform: scale(1);\n        transform: scale(1);\n    }\n}\n\n.content-section {\n    padding-top: 100px;\n}\n\n.download-section {\n    width: 100%;\n    padding: 50px 0;\n    color: #fff;\n    background: url(../img/downloads-bg.jpg) no-repeat center center scroll;\n    background-color: #000;\n    -webkit-background-size: cover;\n    -moz-background-size: cover;\n    background-size: cover;\n    -o-background-size: cover;\n}\n\n@media(min-width:767px) {\n    .content-section {\n        padding-top: 250px;\n    }\n\n    .download-section {\n        padding: 100px 0;\n    }\n}\n\n.btn {\n    border-radius: 0;\n    text-transform: uppercase;\n    font-family: Montserrat,\"Helvetica Neue\",Helvetica,Arial,sans-serif;\n    font-weight: 400;\n    -webkit-transition: all .3s ease-in-out;\n    -moz-transition: all .3s ease-in-out;\n    transition: all .3s ease-in-out;\n}\n\n.btn-default {\n    border: 1px solid #42dca3;\n    color: #42dca3;\n    background-color: transparent;\n}\n\n.btn-default:hover,\n.btn-default:focus {\n    border: 1px solid #42dca3;\n    outline: 0;\n    color: #000;\n    background-color: #42dca3;\n}\n\nul.banner-social-buttons {\n    margin-top: 0;\n}\n\n@media(max-width:1199px) {\n    ul.banner-social-buttons {\n        margin-top: 15px;\n    }\n}\n\n@media(max-width:767px) {\n    ul.banner-social-buttons li {\n        display: block;\n        margin-bottom: 20px;\n        padding: 0;\n    }\n\n    ul.banner-social-buttons li:last-child {\n        margin-bottom: 0;\n    }\n}\n\nfooter {\n    padding: 50px 0;\n}\n\nfooter p {\n    margin: 0;\n}\n\n::-moz-selection {\n    text-shadow: none;\n    background: #fcfcfc;\n    background: rgba(255,255,255,.2);\n}\n\n::selection {\n    text-shadow: none;\n    background: #fcfcfc;\n    background: rgba(255,255,255,.2);\n}\n\nimg::selection {\n    background: 0 0;\n}\n\nimg::-moz-selection {\n    background: 0 0;\n}\n\nbody {\n    webkit-tap-highlight-color: rgba(255,255,255,.2);\n}\n\n.logo {\n    background-color: rgba(0,0,0,0.7);\n    padding: 30px;\n}\n\n.tooltip > .tooltip-inner { background-color: white; color: black; }\n.tooltip > .tooltip-arrow { border-bottom-color: white; color: black; }\n\n.tooltip {\n    opacity: 0.95 !important;\n}\n\n.pixel-tooltip {\n    padding: 0px !important;\n    background-color: transparent !important;\n}\n\n.fa-5 {\n    font-size: 40px !important;\n}\n\n.ico {\n    padding-right: 10px;\n}\n\n\n\n\n\n\n\n.sk-wave {\n  margin: 40px auto;\n  width: 50px;\n  height: 40px;\n  text-align: center;\n  font-size: 10px; }\n  .sk-wave .sk-rect {\n    background-color: #eeeeee;\n    height: 100%;\n    width: 6px;\n    display: inline-block;\n    -webkit-animation: sk-waveStretchDelay 1.2s infinite ease-in-out;\n            animation: sk-waveStretchDelay 1.2s infinite ease-in-out; }\n  .sk-wave .sk-rect1 {\n    -webkit-animation-delay: -1.2s;\n            animation-delay: -1.2s; }\n  .sk-wave .sk-rect2 {\n    -webkit-animation-delay: -1.1s;\n            animation-delay: -1.1s; }\n  .sk-wave .sk-rect3 {\n    -webkit-animation-delay: -1s;\n            animation-delay: -1s; }\n  .sk-wave .sk-rect4 {\n    -webkit-animation-delay: -0.9s;\n            animation-delay: -0.9s; }\n  .sk-wave .sk-rect5 {\n    -webkit-animation-delay: -0.8s;\n            animation-delay: -0.8s; }\n\n@-webkit-keyframes sk-waveStretchDelay {\n  0%, 40%, 100% {\n    -webkit-transform: scaleY(0.4);\n            transform: scaleY(0.4); }\n  20% {\n    -webkit-transform: scaleY(1);\n            transform: scaleY(1); } }\n@keyframes sk-waveStretchDelay {\n  0%, 40%, 100% {\n    -webkit-transform: scaleY(0.4);\n            transform: scaleY(0.4); }\n  20% {\n    -webkit-transform: scaleY(1);\n            transform: scaleY(1); } }\n"
  },
  {
    "path": "web/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n    <meta charset=\"utf-8\">\n    <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <meta name=\"author\" content=\"Taehoon Kim (carpedm20@gmail.com)\">\n    <meta name=\"keywords\" content=\"프사 뉴럴,neural face,dcgan,carpedm20\" />\n    <meta name=\"description\" content=\"Face from AI | 인공지능이 만든 얼굴들\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <meta property=\"og:title\" content=\"Neural Face | 프사 뉴럴\" />\n    <meta property=\"og:url\" content=\"http://carpedm20.github.io/faces/\"/>\n    <meta property=\"og:image\" content=\"https://raw.githubusercontent.com/carpedm20/DCGAN-tensorflow/master/web/img/ogimage.png\" />\n    <meta content=\"width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no\" name=\"viewport\">\n\n    <style type=\"text/css\">\n        body.ko :lang(en) {\n          display: none;\n        }\n        body.en :lang(ko) {\n          display: none;\n        }\n    </style>\n\n    <title lang=\"en\">Neural Face | 프사 뉴럴</title>\n\n    <!-- CSS -->\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css\">\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"//kenwheeler.github.io/slick/slick/slick.css\"/>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"//kenwheeler.github.io/slick/slick/slick-theme.css\"/>\n    <link href=\"css/fakeLoader.css\" rel=\"stylesheet\">\n    <link href=\"css/main.css\" rel=\"stylesheet\">\n\n    <!-- Custom Fonts -->\n    <link href=\"css/font-awesome.min.css\" rel=\"stylesheet\" type=\"text/css\">\n    <link href=\"https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic|Montserrat:400,700\" rel=\"stylesheet\" type=\"text/css\">\n\n    <!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->\n    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->\n    <!--[if lt IE 9]>\n        <script src=\"https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js\"></script>\n        <script src=\"https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js\"></script>\n    <![endif]-->\n</head>\n\n<body id=\"page-top\" data-spy=\"scroll\" data-target=\".navbar-fixed-top\"  class=\"en\">\n    <div id=\"fakeLoader\"></div>\n    <div id=\"fb-root\"></div>\n    <script>(function(d, s, id) {\n      var js, fjs = d.getElementsByTagName(s)[0];\n      if (d.getElementById(id)) return;\n      js = d.createElement(s); js.id = id;\n      js.src = \"//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.5&appId=776839839027483\";\n      fjs.parentNode.insertBefore(js, fjs);\n    }(document, 'script', 'facebook-jssdk'));</script>\n\n    <!-- Navigation -->\n    <nav class=\"navbar navbar-custom navbar-fixed-top\" role=\"navigation\">\n        <div class=\"container\">\n            <div class=\"navbar-header\">\n                <button type=\"button\" class=\"navbar-toggle\" data-toggle=\"collapse\" data-target=\".navbar-main-collapse\">\n                    <i class=\"fa fa-bars\"></i>\n                </button>\n                <a class=\"navbar-brand page-scroll\" href=\"http://carpedm20.github.io/\" target=\"_blank\">\n                    <span class=\"light\">carpedm20</span>\n                </a>\n            </div>\n\n            <div class=\"collapse navbar-collapse navbar-right navbar-main-collapse\">\n                <ul class=\"nav navbar-nav\">\n                    <li class=\"hidden\">\n                        <a href=\"#page-top\"></a>\n                    </li>\n                    <li>\n                        <a class=\"page-scroll\" href=\"#about\">About</a>\n                    </li>\n                    <li>\n                        <a class=\"page-scroll\" href=\"#generate\">Generate</a>\n                    </li>\n                    <li>\n                        <a class=\"page-scroll\" href=\"#details\">Details</a>\n                    </li>\n                    <li>\n                        <a class=\"page-scroll\" href=\"#results\">Results</a>\n                    </li>\n                    <li>\n                        <a class=\"page-scroll\" href=\"#aboutme\">Me?</a>\n                    </li>\n                </ul>\n            </div>\n        </div>\n    </nav>\n\n    <!-- Intro Header -->\n    <header class=\"intro\">\n        <video autoplay loop muted id=\"background\">\n            <source src=\"videos/background.mp4\" type=\"video/mp4\">\n        </video>\n        <div class=\"intro-body\">\n            <div class=\"container\">\n                <div class=\"row\">\n                    <div class=\"col-md-8 col-md-offset-2 col-xs-12 logo\">\n                        <h1 lang=\"ko\" class=\"brand-heading\">프사 뉴럴</h1>\n                        <h2 lang=\"en\" class=\"brand-heading\">Neural Face</h2>\n                        <p lang=\"ko\" class=\"intro-text\">인공지능이 만든 얼굴들</p>\n                        <div class=\"row\">\n                            <button class=\"btn-default\" onclick=\"document.body.className='en'\">English</button>\n                            <button class=\"btn-default\" onclick=\"document.body.className='ko'\">한국어</button>\n                        </div>\n                        <a href=\"#about\" class=\"btn btn-circle page-scroll\">\n                            <i class=\"fa fa-angle-double-down animated\"></i>\n                        </a>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </header>\n\n    <section id=\"about\" class=\"container content-section text-center\">\n        <div class=\"row\">\n            <h2 lang=\"ko\">프사 뉴럴</h2>\n            <h2 lang=\"en\">Neural Face</h2>\n            <div lang=\"ko\" class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <p><b>프사 뉴럴</b>은 Facebook AI Research에서 개발한 <a href=\"http://arxiv.org/pdf/1511.06434v2.pdf\" target=\"_blank\">Deep Convolutional Generative Adversarial Networks</a> (DCGAN) 이라는 기계 학습 모델을 사용해 만들어졌습니다.</p>\n                <p><b>프사 뉴럴</b>은 얼굴 사진을 만드는 인공 지능이며<br>이 페이지에 나오는 모든 사람들은 이세상에 존재하지 않습니다.</p>\n            </div>\n            <div lang=\"en\" class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <p><b>Neural Face</b> uses <a href=\"http://arxiv.org/pdf/1511.06434v2.pdf\" target=\"_blank\">Deep Convolutional Generative Adversarial Networks</a> (DCGAN), which is developed by Facebook AI Research.</p>\n                <p><b>Neural Face</b> is an Artificial Intelligence which generates face images<br>and all images in this page are not REAL.</p>\n            </div>\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <div class=\"fb-like\" data-href=\"http://carpedm20.github.io/faces/\" data-layout=\"button_count\" data-action=\"like\" data-show-faces=\"true\" data-share=\"true\"></div>\n            </div>\n        </div>\n    </section>\n\n\n    <section id=\"generate\" class=\"container content-section text-center\">\n        <div class=\"row\">\n            <h2>Image Generation</h2>\n            <div class=\"col-md-8 col-md-offset-2\">\n                <img src=\"img/single1.gif\">\n                <img src=\"img/single2.gif\">\n                <img src=\"img/single3.gif\">\n                <img src=\"img/single4.gif\">\n            </div>\n            <div lang=\"ko\" class=\"col-md-8 col-md-offset-2 col-xs-12 text-left\">\n                <br/><p><b>프사 뉴럴</b>은 0에서 1 사이의 100개의 숫자 <b>z</b>로 사람의 이미지를 만들어내는 인공지능입니다.</p>\n                <p>1. 아래에 보이는 100개의 픽셀을 <b>z</b>의 각 숫자를 나타냅니다.<br/>2. 만들어진 사진 위에 마우스를 올리면 사진에 사용된 <b>z</b>가 보입니다.<br/>3. 만들어진 이미지를 누르시면 그 이미지의 <b>z</b>가 복사됩니다.</p>\n            </div>\n            <div lang=\"en\" class=\"col-md-8 col-md-offset-2 col-xs-12 text-left\">\n                <br/><p><b>Neural Face</b> uses a vector <b>z</b> that consists of 100 real numbers ranging from 0 to 1.</p>\n                <p>1. Each pixel in the below pallete represents a value in <b>z</b>.<br/>2. If you hover your mouse over an image, <b>z</b> for that image will be displayed.<br/>3. If you click an image, <b>z</b> will be copied to the palette.</p>\n            </div>\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <p lang=\"ko\"><small>(브라우저 성능에 따라 1~10초가 걸립니다)</small></p>\n                <p lang=\"en\"><small>(Might take 1 to 10 seconds depending on your browser)</small></p>\n                <div class=\"row\" id=\"images\">\n                </div>\n                <div class=\"row\">\n                    <br/>\n                    <div class=\"col-md-7 col-xs-8\">\n                        <canvas id=\"pixel\" height='200' width='200' style=\"float:right\" onselectstart=\"this.style.cursor='crosshair'; return false;\">\n                            <p>\n                                Sorry, your browser doesn't support the <canvas> element.\n                            </p>\n                            <p>\n                                Please upgrade to\n                                <a href='http://www.microsoft.com/IE9' target=\"_blank\">IE 9</a>\n                                or use the latest\n                                <a href='http://www.google.com/chrome' target=\"_blank\">Chrome</a>\n                                or\n                                <a href='http://www.mozilla.com/' target=\"_blank\">Firefox</a>\n                            </p>\n                        </canvas>\n                    </div>\n                    <div class=\"col-xs-5\" id=\"colors\">\n                        <div class=\"color\" data-color=\"#000000\">\n                          <div class=\"isometric\" id=\"color_1\" style=\"background-color: #000000\"></div>\n                        </div>\n                        <div class=\"color\" data-color=\"#444444\">\n                          <div class=\"isometric\" id=\"color_2\" style=\"background-color: #444444\"></div>\n                        </div>\n                        <div class=\"color\" data-color=\"#888888\">\n                          <div class=\"isometric\" id=\"color_3\" style=\"background-color: #888888\"></div>\n                        </div>\n                        <div class=\"color\" data-color=\"#CCCCCC\">\n                          <div class=\"isometric\" id=\"color_4\" style=\"background-color: #CCCCCC\"></div>\n                        </div>\n                        <div class=\"color\" data-color=\"#FFFFFF\">\n                          <div class=\"isometric\" id=\"color_5\" style=\"background-color: #FFFFFF\"></div>\n                        </div>\n                    </div>\n                </div>\n                <div id=\"loading\" class=\"row\">\n                    <div class=\"sk-wave\">\n                        <div class=\"sk-rect sk-rect1\"></div>\n                        <div class=\"sk-rect sk-rect2\"></div>\n                        <div class=\"sk-rect sk-rect3\"></div>\n                        <div class=\"sk-rect sk-rect4\"></div>\n                        <div class=\"sk-rect sk-rect5\"></div>\n                    </div>\n                    <div class=\"col-xs-12  text-center\">\n                        <p lang=\"ko\"><b>프사 뉴럴</b>을 불러오고 있습니다...</p>\n                        <p lang=\"en\"><b>Neural Face</b> is preparing to draw...</p>\n                    </div>\n                </div>\n                <div id=\"draw-btn\" class=\"row\" style=\"display: none;\">\n                    <br/>\n                    <button lang=\"ko\" type=\"button\" class=\"btn btn-default draw\" class=\"btn btn-default\">만들기</button>\n                    <button lang=\"ko\" type=\"button\" class=\"btn btn-default shuffle\" class=\"btn btn-default\">랜덤 얼굴</button>\n                    <button lang=\"en\" type=\"button\" class=\"btn btn-default draw\" class=\"btn btn-default\">Generate</button>\n                    <button lang=\"en\" type=\"button\" class=\"btn btn-default shuffle\" class=\"btn btn-default\">Random face</button>\n                </div>\n            </div>\n        </div>\n    </section>\n\n    <section id=\"details\" class=\"container content-section text-center\">\n        <div class=\"row\">\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <h2 lang=\"ko\">알고리즘</h2>\n                <h2 lang=\"en\">Algorithm</h2>\n            </div>\n\n            <img class=\"col-md-4 col-md-offset-4 col-xs-10 col-xs-offset-1\" src=\"https://cosmonio.com/Research/Deep-Learning/files/small_1420.png\">\n\n            <div lang=\"ko\" class=\"col-md-8 col-md-offset-2 col-xs-12 text-left\">\n                <p><b>프사 뉴럴</b>의 핵심 모델인 <a href=\"http://arxiv.org/pdf/1511.06434v2.pdf\" target=\"_blank\">DCGAN</a>은 두 개의 인공 신경망으로 구성되어 있으며, 각각</p>\n                <p>1. 사진을 만들어내는 <b>생성자 (G)</b><br/>2. 진짜 사진과 생성자가 만든 사진을 구분하는 <b>구분자 (D)</b></p>\n                <p>라고 부릅니다.</p>\n                <p>두 신경망은 수많은 이미지를 반복적으로 보면서 생성자는 구분자를 속이기 위해, 구분자는 생성자가 만든 사진을 판별하기 위해 학습합니다. 이러한 학습 방법을 <a href=\"https://en.wikipedia.org/wiki/Adversarial_machine_learning\">적대적 학습 (Adversarial Learning)</a>이라고 하며, 생성자와 구분자를 <b>도둑</b>과 <b>경찰</b>로 비유하기도 합니다.</p>\n            </div>\n            <div lang=\"en\" class=\"col-md-8 col-md-offset-2 col-xs-12 text-left\">\n                <p><a href=\"http://arxiv.org/pdf/1511.06434v2.pdf\" target=\"_blank\">DCGAN</a>, which is the core of <b>Neural Face</b>, consists of two different neural networks which are:</p>\n                <p>1. <b>Generator (G)</b> that generates an image<br/>2. <b>Discriminator (D)</b> that discriminate real images from generated images</p>\n                <p>Two neural networks compete as one tries to deceive the other. This kind of learning is called <a href=\"https://en.wikipedia.org/wiki/Adversarial_machine_learning\">Adversarial Learning</a>. Because of this, <b>Generator</b> and <b>Discriminator</b> are described as a <b>thief</b> and <b>police</b>, respectively.</p>\n            </div>\n            <br/>\n            <img class=\"col-md-6 col-md-offset-3 col-xs-10 col-xs-offset-1\" src=\"img/model.png\">\n\n            <div lang=\"ko\" class=\"col-md-8 col-md-offset-2 col-xs-12 text-left\">\n                <p><br/>생성자와 구분자는 여러 가지 인공 신경망 종류 중에서 각각 <a href=\"https://www.quora.com/How-does-a-deconvolutional-neural-network-work\" target=\"_blank\">Deconvolutional Network (DNN)</a>과 <a href=\"http://cs231n.github.io/convolutional-networks/\" target=\"_blank\">Convolutional Neural Network (CNN)</a>로 구현되어 있습니다. <b>CNN</b>은 수백 개의 픽셀로 이루어진 이미지를 작은 차원의 숫자들 (<b>z</b>)로 잘 요약할 수 있는 필터를 배우는 인공 신경망이며, <b>DNN</b>은 이렇게 작아진 차원의 숫자들로 원래 이미지를 복원하는 필터를 배우는 신경망입니다.</p>\n                <p>구분자는 인공 신경망에 실제 이미지를 넣은 결과를 <b>1</b>로, 만들어진 이미지의 결과는 <b>0</b>으로 구분하도록 학습합니다. 반대로 생성자는 Gaussian Distribution을 따르는 <b>z</b>라는 확률 변수를 두고, 사람의 이미지의 확률 분포를 <b>z</b>를 사용해 계산합니다. 이렇게 만들어진 이미지를 구분자가 실제 이미지라고 잘못 판단하도록 계속 학습합니다.</p>\n            </div>\n            <div lang=\"en\" class=\"col-md-8 col-md-offset-2 col-xs-12 text-left\">\n                <p><br/>Generator and Discriminator consist of <a href=\"https://www.quora.com/How-does-a-deconvolutional-neural-network-work\" target=\"_blank\">Deconvolutional Network (DNN)</a> and <a href=\"http://cs231n.github.io/convolutional-networks/\" target=\"_blank\">Convolutional Neural Network (CNN)</a>. <b>CNN</b> is a neural network which encodes the hundreds of pixels of an image into a vector of small dimensions (<b>z</b>) which is a summary of the image. <b>DNN</b> is a network that learns filters to recover the original image from <b>z</b>.</p>\n                <p>When a real image is given, Discriminator should output <b>1</b> or <b>0</b> for whether the image was generated from Generator. In the contrast, Generator generates an image from <b>z</b>, which follows a Gaussian Distribution, and tries to figure out the distribution of human images from <b>z</b>. In this way, a Generator tries to cheat Discriminator into making a wrong decision.</p>\n            </div>\n        </div>\n    </section>\n\n    <section id=\"results\" class=\"container content-section text-center\">\n        <div class=\"row\">\n            <h2>Results</h2>\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12 text-left\">\n                <p lang=\"ko\"><b>프사 뉴럴</b>를 학습시키기 위해 인터넷에 10만 개 이상의 사진들을 모았고 이 사진들에서 얼굴 사진만 잘라서 얼굴 데이터 셋을 만들었습니다. 코드는 최근에 구글에서 공개한 <a href=\"https://www.tensorflow.org/\" target=\"_blank\">TensorFlow</a>로 구현했으며 GTX 980 Ti를 사용하여 이틀간 학습시켰습니다.</p>\n                <p lang=\"ko\">아래는 초기 학습 단계에서 프사 뉴럴이 정해진 <b>z</b>로 얼굴 사진을 만들어 가는 과정을 보여줍니다.</p>\n                <p lang=\"en\">More than 100K images are crawled from online communities and those images are cropped by using <a href=\"https://github.com/cmusatyalab/openface\">openface</a> which is a face recognition framework. <b>Neural Face</b> is implemented with <a href=\"https://www.tensorflow.org/\" target=\"_blank\">TensorFlow</a> and a GTX 980 Ti is used to train for two days.</p>\n                <p lang=\"en\">Below is a series of images generated by <b>Generator</b> with a fixed <b>z</b> between the first and the fith epoch of training.</p>\n                <video autoplay loop muted class=\"col-md-6 col-md-offset-3 col-xs-10 col-xs-offset-1\">\n                    <source src=\"videos/training.mp4\" type=\"video/mp4\">\n                </video>\n            </div>\n\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12 text-left\">\n                <p lang=\"ko\"><br/><br/>생성자가 사용하는 <b>z</b>는 -1에서 1 사이의 <a href=\"https://ko.wikipedia.org/wiki/%EC%A0%95%EA%B7%9C%EB%B6%84%ED%8F%AC\">Gaussian Distribution</a>을 따르는 확률 변수이며, 평균값인 <b>0</b>으로 이미지를 만들게 되면, <b>프사 뉴럴</b>이 생각하는 평균적인 얼굴을 알 수 있습니다.</p>\n                <p lang=\"en\"><br/><br/>The vector <b>z</b> has real values from -1 to 1 and it follows the <a href=\"https://en.wikipedia.org/wiki/Normal_distribution\">Gaussian Distribution</a>. We can see the most common face that is interpreted by <b>Neural Face</b> using <b>0</b> as all values of <b>z</b>.</p>\n            </div>\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <img src=\"img/average.png\"/>\n            </div>\n\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12 text-left\">\n                <p lang=\"ko\"><br/><br/>평균값 <b>0</b>에서 랜덤한 차원의 값을 조금씩 바꾸면 아래와 같은 변화를 볼 수 있습니다.</p>\n                <p lang=\"en\"><br/><br/>The below images are generated by changing the values of <b>z</b> continuously, starting from the average value (<b>0</b>) to -1 or 1.</p>\n            </div>\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <video autoplay loop muted class=\"col-md-6 col-md-offset-3 col-xs-10 col-xs-offset-1\">\n                    <source src=\"videos/random.mp4\" type=\"video/mp4\">\n                </video>\n            </div>\n\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12 text-left\">\n                <p lang=\"ko\"><br/><br/>아래의 사진들은 100차원의 <b>z</b> 값 중에서 임의의 차원들을 -1부터 1까지 바꾸면서 <b>생성자</b> 신경망에 넣은 결과이며, 점점 미소를 짓거나, 안경이 생기거나, 흑백 사진이 되거나, 성별이 바뀌는 등의 결과를 확인하실 수 있습니다.</p>\n                <p lang=\"en\"><br/><br/>The below images are generated by changing ten different values of <b>z</b> from -1 to 1. People in the images vary in characteristics such as smiling, wearing glasses, turning into black and white images, and changing into different sex.</p>\n            </div>\n        </div>\n        <div class=\"row\">\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <div class=\"slick\">\n                    <img src=\"img/change1.png\"/>\n                    <img src=\"img/change2.png\"/>\n                    <img src=\"img/change3.png\"/>\n                    <img src=\"img/change4.png\"/>\n                    <img src=\"img/change5.png\"/>\n                    <img src=\"img/change6.png\"/>\n                </div>\n            </div>\n        </div>\n        <div class=\"row\">\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <p lang=\"ko\"><br><br><b>프사 뉴럴</b>의 코드는 <a href=\"https://github.com/carpedm20/DCGAN-tensorflow\" target=\"_blank\">이곳</a>에 공개되어 있습니다.</p>\n                <p lang=\"en\"><br><br>The code of <b>Neural Face</b> can be found <a href=\"https://github.com/carpedm20/DCGAN-tensorflow\" target=\"_blank\">here</a>.</p>\n            </div>\n        </div>\n    </section>\n\n    <section id=\"results\" class=\"container content-section text-center\">\n        <div class=\"row\">\n            <h2>Misc.</h2>\n            <div lang=\"ko\" class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <p>마지막으로 <a href=\"https://namu.wiki/w/%ED%8A%9C%EB%A7%81%20%ED%85%8C%EC%8A%A4%ED%8A%B8\">튜링 테스트</a>를 해 보겠습니다 :)<br>아래 사진 중에서 진짜 사진은 무엇일까요?</p>\n                <p><small>(마우스로 클릭하면 정답이 보입니다)</small></p>\n            </div>\n            <div lang=\"en\" class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <p>Lastly, let's conduct a <a href=\"https://en.wikipedia.org/wiki/Turing_test\">Turing Test</a> :)<br>Can you guess which are the real images?</p>\n                <p><small>(Answer will be showed if you click an image)</small></p>\n            </div>\n        </div>\n        <div class=\"row\">\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <div class=\"turing-slick\">\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"true.\" src=\"img/t1.jpg\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f1.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f2.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f3.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"true.\" src=\"img/t6.jpg\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f4.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f5.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f6.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"true.\" src=\"img/t2.jpg\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f7.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"true.\" src=\"img/t7.jpg\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f8.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f9.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f17.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"true.\" src=\"img/t3.jpg\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f10.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f11.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f12.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f13.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"true.\" src=\"img/t4.jpg\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f14.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f15.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"fake!\" src=\"img/f16.png\"/>\n                    <img data-container=\"body\" data-toggle=\"tooltip\" title=\"true.\" src=\"img/t5.jpg\"/>\n                </div>\n            </div>\n        </div>\n        <div class=\"row\">\n            <div class=\"col-md-8 col-md-offset-2 col-xs-12\">\n                <br/></br/>\n                <br/></br/>\n                <br/></br/>\n                <h2>Other Projects</h2>\n                <ul style=\"list-style-type:none; padding: 0; font-size: 20px;\">\n                    <li><a href=\"https://github.com/carpedm20/MemN2N-tensorflow\" target=\"_blank\">Memory Networks</a></li>\n                    <li><a href=\"https://github.com/carpedm20/NTM-tensorflow\" target=\"_blank\">Neural Turing Machine</a></li>\n                    <li><a href=\"https://github.com/carpedm20/lstm-char-cnn-tensorflow\" target=\"_blank\">LSTM Character CNN Networks</a></li>\n                </ul>\n            </div>\n        </div>\n    </section>\n\n    <!-- Contact Section -->\n    <section id=\"aboutme\" class=\"container content-section text-center\">\n        <div class=\"row\">\n            <div class=\"col-lg-8 col-lg-offset-2\">\n                <h3>Taehoon Kim</h3>\n                <h3><a href=\"http://carpedm20.github.io/\" target=\"_blank\">@carpedm20</a></h3>\n\n                <a class=\"ico\" href=\"https://github.com/carpedm20/\" target=\"_blank\">\n                    <i class=\"fa fa-github-square fa-5\"></i>\n                </a>\n                <a class=\"ico\" href=\"https://www.linkedin.com/in/carpedm20\" target=\"_blank\">\n                    <i class=\"fa fa-linkedin-square fa-5\"></i>\n                </a>\n                <a class=\"ico\" href=\"https://www.facebook.com/carpedm20\" target=\"_blank\">\n                    <i class=\"fa fa-facebook-square fa-5\"></i>\n                </a>\n                <a href=\"https://twitter.com/carpedm20\" target=\"_blank\">\n                    <i class=\"fa fa-twitter-square fa-5\"></i>\n                </a>\n            </div>\n        </div>\n    </section>\n\n    <!-- Footer -->\n    <footer>\n        <div class=\"container content-section text-center\">\n        </div>\n    </footer>\n\n    <!-- jQuery -->\n    <script src=\"//code.jquery.com/jquery-2.2.0.min.js\"></script>\n    <script src=\"js/vendor/fakeLoader.min.js\"></script>\n    <script>\n        $(\"#fakeLoader\").fakeLoader({\n            zIndex:\"999\",//Default zIndex\n            spinner:\"spinner3\",\n            bgColor:\"black\"\n        })\n    </script>\n\n    <script type=\"text/javascript\" src=\"https://kenwheeler.github.io/slick/slick/slick.js\"></script>\n    <script src=\"//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js\"></script>\n    <script src=\"js/vendor/jquery.easing.min.js\"></script>\n    <script src=\"js/vendor/pixel.min.js\"></script>\n\n    <script src=\"js/convnet.js\"></script>\n    <script src=\"js/layers.js\"></script>\n    <script src=\"js/app.js\"></script>\n\n    <!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->\n    <script>\n        (function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=\n        function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;\n        e=o.createElement(i);r=o.getElementsByTagName(i)[0];\n        e.src='https://www.google-analytics.com/analytics.js';\n        r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));\n        ga('create','UA-73375683-1','auto');ga('send','pageview');\n    </script>\n</body>\n\n</html>\n"
  },
  {
    "path": "web/js/app.js",
    "content": "\nwindow.mobilecheck = function() {\n  var check = false;\n  (function(a){if(/(android|bb\\d+|meego).+mobile|avantgo|bada\\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\\-(n|u)|c55\\/|capi|ccwa|cdm\\-|cell|chtm|cldc|cmd\\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\\-s|devi|dica|dmob|do(c|p)o|ds(12|\\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\\-|_)|g1 u|g560|gene|gf\\-5|g\\-mo|go(\\.w|od)|gr(ad|un)|haie|hcit|hd\\-(m|p|t)|hei\\-|hi(pt|ta)|hp( i|ip)|hs\\-c|ht(c(\\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\\-(20|go|ma)|i230|iac( |\\-|\\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\\/)|klon|kpt |kwc\\-|kyo(c|k)|le(no|xi)|lg( g|\\/(k|l|u)|50|54|\\-[a-w])|libw|lynx|m1\\-w|m3ga|m50\\/|ma(te|ui|xo)|mc(01|21|ca)|m\\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\\-2|po(ck|rt|se)|prox|psio|pt\\-g|qa\\-a|qc(07|12|21|32|60|\\-[2-7]|i\\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\\-|oo|p\\-)|sdk\\/|se(c(\\-|0|1)|47|mc|nd|ri)|sgh\\-|shar|sie(\\-|m)|sk\\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\\-|v\\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\\-|tdg\\-|tel(i|m)|tim\\-|t\\-mo|to(pl|sh)|ts(70|m\\-|m3|m5)|tx\\-9|up(\\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\\-|your|zeto|zte\\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera);\n  return check;\n}\n\nfunction rgb2hex(rgb){\n    rgb = rgb.match(/^rgba?[\\s+]?\\([\\s+]?(\\d+)[\\s+]?,[\\s+]?(\\d+)[\\s+]?,[\\s+]?(\\d+)[\\s+]?/i);\n    return (rgb && rgb.length === 4) ?\n        (\"0\" + parseInt(rgb[1],10).toString(16)).slice(-2) +\n        (\"0\" + parseInt(rgb[2],10).toString(16)).slice(-2) +\n        (\"0\" + parseInt(rgb[3],10).toString(16)).slice(-2) : \"\";\n}\n\nvar make_z = function(max_length, scale) {\n    var z = []\n    var scale = scale | 2;\n    while(z.length < max_length){\n      var randomnumber=Math.random() * scale;\n      var found=false;\n      for(var i=0;i<z.length;i++){\n        if(z[i]==randomnumber){found=true;break}\n      }\n      if(!found)z[z.length]=randomnumber;\n    }\n    return z;\n}\n\nvar get_pixels = function() {\n    var x = new Array(100);\n    var frame = PIXEL.getCurrentFrame();\n\n    for (var i=0;  i < 10;  i++) {\n        for (var j=0;  j < 10;  j++) {\n            var idx = i*10 + j;\n            if (frame[i][j] == \"rgba(0, 0, 0, 0)\") {\n                x[idx] = 0;\n            } else {\n                x[idx] = ((parseInt(frame[i][j].substring(1,3) ,16)/255*2)-1)/2;\n            }\n        }\n    }\n    return x;\n}\n\nvar recover_pixels = function(x) {\n    for (var i=0;  i < 10;  i++) {\n        for (var j=0;  j < 10;  j++) {\n            var idx = i*10 + j;\n            x[idx] = ((x[idx] * 2) + 1) / 2 * 255;\n        }\n    }\n    return x;\n}\n\nvar draw_pixels = function(x) {\n    var frame = PIXEL.getCurrentFrame();\n\n    for (var i=0;  i < 10;  i++) {\n        for (var j=0;  j < 10;  j++) {\n            var hex = Math.ceil(x[i*10+j]).toString(16);\n            PIXEL.setDraw(true);\n            PIXEL.doAction(i*20, j*20, \"#\" + hex + hex + hex);\n            PIXEL.setDraw(false);\n        }\n    }\n}\n\nvar clip_pixel = function(x){\n    if(x>1) return 255;\n    else if(x<-1) return 0;\n    else return 255*(x+1.0)/2.0;\n}\n\nfunction cloneCanvas(oldCanvas) {\n\n    //create a new canvas\n    var newCanvas = document.createElement('canvas');\n    var context = newCanvas.getContext('2d');\n\n    //set dimensions\n    newCanvas.width = oldCanvas.width;\n    newCanvas.height = oldCanvas.height;\n\n    //apply the old canvas to the new one\n    context.drawImage(oldCanvas, 0, 0);\n\n    //return the new canvas\n    return newCanvas;\n}\n\n$( document ).ready(function() {\n    draw_pixels(make_z(100, 255));\n\n    $('.slick').slick({\n        slidesToShow: 2,\n        autoplay: true,\n        dots: true,\n        autoplaySpeed: 3000,\n        responsive: [\n            {\n                breakpoint: 980,\n                settings: {\n                    slidesToShow: 1,\n                    slidesToScroll: 1\n                }\n            }\n        ]\n    });\n    $('.turing-slick').slick({\n        slidesToShow: 6,\n        autoplay: true,\n        dots: true,\n        autoplaySpeed: 3000,\n        responsive: [\n            {\n                breakpoint: 1200,\n                settings: {\n                    slidesToShow: 5,\n                    slidesToScroll: 5\n                }\n            },\n            {\n                breakpoint: 980,\n                settings: {\n                    slidesToShow: 3,\n                    slidesToScroll: 3\n                }\n            }\n        ]\n    });\n\n    $(\"[data-toggle=tooltip]\").tooltip();\n\n    var layer_defs = [];\n    layer_defs.push({type:\"input\", out_sx:1, out_sy:1, out_depth:100});\n    layer_defs.push({type:\"deconv\", sx:4, filters:512, stride:1, pad:0, bn:true, activation:\"relu\"});\n    layer_defs.push({type:\"deconv\", sx:4, filters:256, stride:2, pad:1, bn:true, activation:\"relu\"});\n    layer_defs.push({type:\"deconv\", sx:4, filters:128, stride:2, pad:1, bn:true, activation:\"relu\"});\n    layer_defs.push({type:\"deconv\", sx:4, filters:64, stride:2, pad:1, bn:true, activation:\"relu\"});\n    layer_defs.push({type:\"deconv\", sx:4, filters:3, stride:2, pad:1, activation:\"tanh\"});\n\n    var net = new convnetjs.Net();\n    net.makeLayers(layer_defs);\n\n    net.layers[1].fromJSON(layer_0);\n    net.layers[3].fromJSON(layer_1);\n    net.layers[5].fromJSON(layer_2);\n    net.layers[7].fromJSON(layer_3);\n    net.layers[9].fromJSON(layer_4);\n\n    var input = new convnetjs.Vol(1, 1, 100, 0.0);\n\n    var duplicates = [];\n    var pixels = [];\n\n    var draw = function() {\n        cur_pixel = get_pixels();\n        input.w = cur_pixel;\n\n        var output = net.forward(input);\n        var scale = 2;\n        var W = output.sx * scale;\n        var H = output.sy * scale;\n\n        var canv = document.createElement(\"canvas\");\n        canv.width = W;\n        canv.height = H;\n\n        var ctx = canv.getContext(\"2d\");\n        var g = ctx.createImageData(W, H);\n\n        for(var d=0; d < 3; d++) {\n            for(var x=0; x < output.sx; x++) {\n                for(var y=0; y < output.sy; y++) {\n                    var dval = clip_pixel(output.get(x,y,d));\n\n                    for(var dx = 0; dx < scale; dx++) {\n                        for(var dy =0 ;dy < scale; dy++) {\n                            var pp = ((W * (y*scale + dy)) + (dx + x*scale)) * 4;\n                            g.data[pp + d] = dval;\n                            if(d===0) g.data[pp+3] = 255; // alpha channel\n                        }\n                    }\n                }\n            }\n        }\n        ctx.putImageData(g, 0, 0);\n        document.getElementById(\"images\").appendChild(canv);\n\n        duplicates.push(cloneCanvas(document.getElementById(\"pixel\")));\n        pixels.push(cur_pixel);\n\n        $(canv).tooltip({\n                html: true,\n                template: '<div class=\"tooltip\"><div class=\"tooltip-inner pixel-tooltip\"></div></div>',\n                title: function(e) {\n                     var duplicated = duplicates[parseInt($(this).attr(\"id\")) - 1];\n                     return duplicated;\n                },\n                }).hide()\n                .attr(\"id\", duplicates.length)\n                .fadeIn(1000)\n                .click(function() {\n                    draw_pixels(recover_pixels(pixels[parseInt($(this).attr(\"id\")) - 1]));\n                });\n    }\n\n    $(\".draw\").click(draw);\n    $(\".shuffle\").click(function() {\n        draw_pixels(make_z(100, 255));\n        draw();\n    });\n\n    $(\"#loading\").hide();\n    $(\"#draw-btn\").show();\n\n    if (!mobilecheck()) {\n        draw();\n    }\n\n    $(\"#fakeLoader\").fadeOut(3000);\n});\n\n\n// deactivate element\nfunction deactivate($el) {\n    return $el.removeClass(\"active\");\n}\n\n// activate element\nfunction activate($el) {\n    return $el.addClass(\"active\");\n}\n\n// disable element\nfunction disable($el) {\n    return $el.addClass(\"disabled\");\n}\n\n// enable element\nfunction enable($el) {\n    return $el.removeClass(\"disabled\");\n}\n\n// is element enabled?\nfunction isEnabled($el) {\n    return $el.size() > 0 && !$el.hasClass(\"disabled\");\n}\n\n// track event\nfunction trackEvent(e, url) {\n    _gaq.push(['_trackEvent', 'Drawings', e, url]);\n}\n\n// returns mouse or tap event relative coordinates\nfunction getCoordinates(e) {\n    var x, y;\n    \n    x = e.offsetX ? e.offsetX : e.pageX - e.target.parentNode.offsetLeft;\n    y = e.offsetY ? e.offsetY : e.pageY - e.target.parentNode.offsetTop;\n    \n    return {x: x, y: y};\n}\n\nvar currentColor = \"#000000\",\n    copyFrameIndex = -1,\n    tips = true;\n\n// mouse down event callback\nfunction mouseDownCallback(e) {\n    PIXEL.setDraw(true);\n    var coordinates = getCoordinates(e);\n\n    PIXEL.doAction(coordinates.x, coordinates.y, currentColor);\n}\n\n// mouse move event callback\nfunction mouseMoveCallback(e) {\n    var coordinates = getCoordinates(e);\n    \n    PIXEL.doAction(coordinates.x, coordinates.y, currentColor);\n    e.preventDefault();\n}\n\n// mouse up event callback\nfunction mouseUpCallback() {\n    PIXEL.setDraw(false);\n}\n\nvar canvas = $(\"#pixel\");\nPIXEL.init(canvas[0], true);\n\n// set drawing on mousedown\ncanvas.mousedown(mouseDownCallback).mousemove(mouseMoveCallback);\ncanvas.bind('touchstart', mouseDownCallback).bind('touchmove', mouseMoveCallback);\n\n// reset drawing on mouseup\n$(document).mouseup(mouseUpCallback);\n$(document).bind('touchend', mouseUpCallback);\n\n$(\".action.selectable\").click(function() {\n    PIXEL.setAction($(this).data('action'));\n    \n    deactivate($(\".action.selectable.active\"));\n    activate($(this));\n});\n\n// colors\n$(\".color\").click(function() {\n    currentColor = $(this).data('color');\n    \n    deactivate($(\".color.active\"));\n    activate($(this));\n});\n\n// undo\n$(\".undo\").click(function() {\n    PIXEL.undo();\n});\n\n// copy\n$(\".copy\").click(function() {\n    copyFrameIndex = PIXEL.getCurrentFrameId();\n});\n\n$(\".paste\").click(function() {\n    if(copyFrameIndex > -1 && copyFrameIndex < PIXEL.getFramesLength()) {\n        PIXEL.pasteFrameAt(copyFrameIndex);\n    }\n});\n\n$(\".rotate\").click(function() {\n    PIXEL.rotate();\n});\n\n\n\n// jQuery to collapse the navbar on scroll\nfunction collapseNavbar() {\n    if ($(\".navbar\").offset().top > 50) {\n        $(\".navbar-fixed-top\").addClass(\"top-nav-collapse\");\n    } else {\n        $(\".navbar-fixed-top\").removeClass(\"top-nav-collapse\");\n    }\n}\n\n$(window).scroll(collapseNavbar);\n$(document).ready(collapseNavbar);\n\n// jQuery for page scrolling feature - requires jQuery Easing plugin\n$(function() {\n    $('a.page-scroll').bind('click', function(event) {\n        var $anchor = $(this);\n        $('html, body').stop().animate({\n            scrollTop: $($anchor.attr('href')).offset().top\n        }, 1500, 'easeInOutExpo');\n        event.preventDefault();\n    });\n});\n\n// Closes the Responsive Menu on Menu Item Click\n$('.navbar-collapse ul li a').click(function() {\n  if ($(this).attr('class') != 'dropdown-toggle active' && $(this).attr('class') != 'dropdown-toggle') {\n    $('.navbar-toggle:visible').click();\n  }\n});\n\n"
  },
  {
    "path": "web/js/convnet.js",
    "content": "var convnetjs = convnetjs || { REVISION: 'ALPHA' };\n(function(global) {\n  \"use strict\";\n\n  // Random number utilities\n  var return_v = false;\n  var v_val = 0.0;\n  var gaussRandom = function() {\n    if(return_v) { \n      return_v = false;\n      return v_val; \n    }\n    var u = 2*Math.random()-1;\n    var v = 2*Math.random()-1;\n    var r = u*u + v*v;\n    if(r == 0 || r > 1) return gaussRandom();\n    var c = Math.sqrt(-2*Math.log(r)/r);\n    v_val = v*c; // cache this\n    return_v = true;\n    return u*c;\n  }\n  var randf = function(a, b) { return Math.random()*(b-a)+a; }\n  var randi = function(a, b) { return Math.floor(Math.random()*(b-a)+a); }\n  var randn = function(mu, std){ return mu+gaussRandom()*std; }\n\n  // Array utilities\n  var zeros = function(n) {\n    if(typeof(n)==='undefined' || isNaN(n)) { return []; }\n    if(typeof ArrayBuffer === 'undefined') {\n      // lacking browser support\n      var arr = new Array(n);\n      for(var i=0;i<n;i++) { arr[i]= 0; }\n      return arr;\n    } else {\n      return new Float64Array(n);\n    }\n  }\n\n  function average(data, nChannel){\n    var ans = zeros(nChannel);\n    for(var i=0; i < data.length; i++) {\n      ans[i % nChannel] += data[i];\n    }\n    var n = data.length / nChannel;\n    for(var i=0; i < nChannel; i++) {\n      ans[i] = ans[i] / n;\n    }\n    return ans;\n  }\n\n  function standardDeviation(v, nChannel){\n    var values = new Float64Array(v.length);\n    for(var i=0;i<v.length;i++) {\n      values[i] = v[i];\n    }\n    var avgs = average(values, nChannel);\n    \n    for(var i=0; i < values.length; i++) {\n      var diff = (values[i] - avgs[i % nChannel]);\n      values[i] = diff * diff;\n    }\n\n    var stdDev = average(values, nChannel);\n\n    for(var i=0; i < nChannel; i++) {\n      stdDev[i] = Math.sqrt(stdDev[i] + 0.00001);\n    }\n    return stdDev;\n  }\n\n  var arrContains = function(arr, elt) {\n    for(var i=0,n=arr.length;i<n;i++) {\n      if(arr[i]===elt) return true;\n    }\n    return false;\n  }\n\n  var arrUnique = function(arr) {\n    var b = [];\n    for(var i=0,n=arr.length;i<n;i++) {\n      if(!arrContains(b, arr[i])) {\n        b.push(arr[i]);\n      }\n    }\n    return b;\n  }\n\n  // return max and min of a given non-empty array.\n  var maxmin = function(w) {\n    if(w.length === 0) { return {}; } // ... ;s\n    var maxv = w[0];\n    var minv = w[0];\n    var maxi = 0;\n    var mini = 0;\n    var n = w.length;\n    for(var i=1;i<n;i++) {\n      if(w[i] > maxv) { maxv = w[i]; maxi = i; } \n      if(w[i] < minv) { minv = w[i]; mini = i; } \n    }\n    return {maxi: maxi, maxv: maxv, mini: mini, minv: minv, dv:maxv-minv};\n  }\n\n  // create random permutation of numbers, in range [0...n-1]\n  var randperm = function(n) {\n    var i = n,\n        j = 0,\n        temp;\n    var array = [];\n    for(var q=0;q<n;q++)array[q]=q;\n    while (i--) {\n        j = Math.floor(Math.random() * (i+1));\n        temp = array[i];\n        array[i] = array[j];\n        array[j] = temp;\n    }\n    return array;\n  }\n\n  // sample from list lst according to probabilities in list probs\n  // the two lists are of same size, and probs adds up to 1\n  var weightedSample = function(lst, probs) {\n    var p = randf(0, 1.0);\n    var cumprob = 0.0;\n    for(var k=0,n=lst.length;k<n;k++) {\n      cumprob += probs[k];\n      if(p < cumprob) { return lst[k]; }\n    }\n  }\n\n  // syntactic sugar function for getting default parameter values\n  var getopt = function(opt, field_name, default_value) {\n    if(typeof field_name === 'string') {\n      // case of single string\n      return (typeof opt[field_name] !== 'undefined') ? opt[field_name] : default_value;\n    } else {\n      // assume we are given a list of string instead\n      var ret = default_value;\n      for(var i=0;i<field_name.length;i++) {\n        var f = field_name[i];\n        if (typeof opt[f] !== 'undefined') {\n          ret = opt[f]; // overwrite return value\n        }\n      }\n      return ret;\n    }\n  }\n\n  function assert(condition, message) {\n    if (!condition) {\n      message = message || \"Assertion failed\";\n      if (typeof Error !== \"undefined\") {\n        throw new Error(message);\n      }\n      throw message; // Fallback\n    }\n  }\n\n  global.average = average;\n  global.standardDeviation = standardDeviation;\n  global.randi = randi;\n  global.randn = randn;\n  global.zeros = zeros;\n  global.maxmin = maxmin;\n  global.randperm = randperm;\n  global.weightedSample = weightedSample;\n  global.arrUnique = arrUnique;\n  global.arrContains = arrContains;\n  global.getopt = getopt;\n  global.assert = assert;\n  \n})(convnetjs);\n(function(global) {\n  \"use strict\";\n\n  // Vol is the basic building block of all data in a net.\n  // it is essentially just a 3D volume of numbers, with a\n  // width (sx), height (sy), and depth (depth).\n  // it is used to hold data for all filters, all volumes,\n  // all weights, and also stores all gradients w.r.t. \n  // the data. c is optionally a value to initialize the volume\n  // with. If c is missing, fills the Vol with random numbers.\n  var Vol = function(sx, sy, depth, c) {\n    // this is how you check if a variable is an array. Oh, Javascript :)\n    if(Object.prototype.toString.call(sx) === '[object Array]') {\n      // we were given a list in sx, assume 1D volume and fill it up\n      this.sx = 1;\n      this.sy = 1;\n      this.depth = sx.length;\n      // we have to do the following copy because we want to use\n      // fast typed arrays, not an ordinary javascript array\n      this.w = global.zeros(this.depth);\n      this.dw = global.zeros(this.depth);\n      for(var i=0;i<this.depth;i++) {\n        this.w[i] = sx[i];\n      }\n    } else {\n      // we were given dimensions of the vol\n      this.sx = sx;\n      this.sy = sy;\n      this.depth = depth;\n      var n = sx*sy*depth;\n      this.w = global.zeros(n);\n      this.dw = global.zeros(n);\n      if(typeof c === 'undefined') {\n        // weight normalization is done to equalize the output\n        // variance of every neuron, otherwise neurons with a lot\n        // of incoming connections have outputs of larger variance\n        var scale = Math.sqrt(1.0/(sx*sy*depth));\n        for(var i=0;i<n;i++) { \n          this.w[i] = global.randn(0.0, scale);\n        }\n      } else {\n        for(var i=0;i<n;i++) { \n          this.w[i] = c;\n        }\n      }\n    }\n  }\n\n  Vol.prototype = {\n    get: function(x, y, d) { \n      var ix=((this.sx * y)+x)*this.depth+d;\n      return this.w[ix];\n    },\n    set: function(x, y, d, v) { \n      var ix=((this.sx * y)+x)*this.depth+d;\n      this.w[ix] = v; \n    },\n    add: function(x, y, d, v) { \n      var ix=((this.sx * y)+x)*this.depth+d;\n      this.w[ix] += v; \n    },\n    get_grad: function(x, y, d) { \n      var ix = ((this.sx * y)+x)*this.depth+d;\n      return this.dw[ix]; \n    },\n    set_grad: function(x, y, d, v) { \n      var ix = ((this.sx * y)+x)*this.depth+d;\n      this.dw[ix] = v; \n    },\n    add_grad: function(x, y, d, v) { \n      var ix = ((this.sx * y)+x)*this.depth+d;\n      this.dw[ix] += v; \n    },\n    cloneAndZero: function() { return new Vol(this.sx, this.sy, this.depth, 0.0)},\n    clone: function() {\n      var V = new Vol(this.sx, this.sy, this.depth, 0.0);\n      var n = this.w.length;\n      for(var i=0;i<n;i++) { V.w[i] = this.w[i]; }\n      return V;\n    },\n    addFrom: function(V) { for(var k=0;k<this.w.length;k++) { this.w[k] += V.w[k]; }},\n    addFromScaled: function(V, a) { for(var k=0;k<this.w.length;k++) { this.w[k] += a*V.w[k]; }},\n    setConst: function(a) { for(var k=0;k<this.w.length;k++) { this.w[k] = a; }},\n\n    toJSON: function() {\n      // todo: we may want to only save d most significant digits to save space\n      var json = {}\n      json.sx = this.sx; \n      json.sy = this.sy;\n      json.depth = this.depth;\n      json.w = this.w;\n      return json;\n      // we wont back up gradients to save space\n    },\n    fromJSON: function(json) {\n      this.sx = json.sx;\n      this.sy = json.sy;\n      this.depth = json.depth;\n\n      var n = this.sx*this.sy*this.depth;\n      this.w = global.zeros(n);\n      this.dw = global.zeros(n);\n      // copy over the elements.\n      for(var i=0;i<n;i++) {\n        this.w[i] = json.w[i];\n      }\n    }\n  }\n\n  global.Vol = Vol;\n})(convnetjs);\n(function(global) {\n  \"use strict\";\n  var Vol = global.Vol; // convenience\n\n  // Volume utilities\n  // intended for use with data augmentation\n  // crop is the size of output\n  // dx,dy are offset wrt incoming volume, of the shift\n  // fliplr is boolean on whether we also want to flip left<->right\n  var augment = function(V, crop, dx, dy, fliplr) {\n    // note assumes square outputs of size crop x crop\n    if(typeof(fliplr)==='undefined') var fliplr = false;\n    if(typeof(dx)==='undefined') var dx = global.randi(0, V.sx - crop);\n    if(typeof(dy)==='undefined') var dy = global.randi(0, V.sy - crop);\n    \n    // randomly sample a crop in the input volume\n    var W;\n    if(crop !== V.sx || dx!==0 || dy!==0) {\n      W = new Vol(crop, crop, V.depth, 0.0);\n      for(var x=0;x<crop;x++) {\n        for(var y=0;y<crop;y++) {\n          if(x+dx<0 || x+dx>=V.sx || y+dy<0 || y+dy>=V.sy) continue; // oob\n          for(var d=0;d<V.depth;d++) {\n           W.set(x,y,d,V.get(x+dx,y+dy,d)); // copy data over\n          }\n        }\n      }\n    } else {\n      W = V;\n    }\n\n    if(fliplr) {\n      // flip volume horziontally\n      var W2 = W.cloneAndZero();\n      for(var x=0;x<W.sx;x++) {\n        for(var y=0;y<W.sy;y++) {\n          for(var d=0;d<W.depth;d++) {\n           W2.set(x,y,d,W.get(W.sx - x - 1,y,d)); // copy data over\n          }\n        }\n      }\n      W = W2; //swap\n    }\n    return W;\n  }\n\n  // img is a DOM element that contains a loaded image\n  // returns a Vol of size (W, H, 4). 4 is for RGBA\n  var img_to_vol = function(img, convert_grayscale) {\n\n    if(typeof(convert_grayscale)==='undefined') var convert_grayscale = false;\n\n    var canvas = document.createElement('canvas');\n    canvas.width = img.width;\n    canvas.height = img.height;\n    var ctx = canvas.getContext(\"2d\");\n\n    // due to a Firefox bug\n    try {\n      ctx.drawImage(img, 0, 0);\n    } catch (e) {\n      if (e.name === \"NS_ERROR_NOT_AVAILABLE\") {\n        // sometimes happens, lets just abort\n        return false;\n      } else {\n        throw e;\n      }\n    }\n\n    try {\n      var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);\n    } catch (e) {\n      if(e.name === 'IndexSizeError') {\n        return false; // not sure what causes this sometimes but okay abort\n      } else {\n        throw e;\n      }\n    }\n\n    // prepare the input: get pixels and normalize them\n    var p = img_data.data;\n    var W = img.width;\n    var H = img.height;\n    var pv = []\n    for(var i=0;i<p.length;i++) {\n      pv.push(p[i]/255.0-0.5); // normalize image pixels to [-0.5, 0.5]\n    }\n    var x = new Vol(W, H, 4, 0.0); //input volume (image)\n    x.w = pv;\n\n    if(convert_grayscale) {\n      // flatten into depth=1 array\n      var x1 = new Vol(W, H, 1, 0.0);\n      for(var i=0;i<W;i++) {\n        for(var j=0;j<H;j++) {\n          x1.set(i,j,0,x.get(i,j,0));\n        }\n      }\n      x = x1;\n    }\n\n    return x;\n  }\n  \n  global.augment = augment;\n  global.img_to_vol = img_to_vol;\n\n})(convnetjs);\n(function(global) {\n  \"use strict\";\n  var Vol = global.Vol; // convenience\n\n  // This file contains all layers that do dot products with input,\n  // but usually in a different connectivity pattern and weight sharing\n  // schemes: \n  // - FullyConn is fully connected dot products \n  // - ConvLayer does convolutions (so weight sharing spatially)\n  // - DeconvLayer does deconvolutions (so weight sharing spatially)\n  // putting them together in one file because they are very similar\n  var ConvLayer = function(opt) {\n    var opt = opt || {};\n\n    // required\n    this.out_depth = opt.filters;\n    this.sx = opt.sx; // filter size. Should be odd if possible, it's cleaner.\n    this.in_depth = opt.in_depth;\n    this.in_sx = opt.in_sx;\n    this.in_sy = opt.in_sy;\n    \n    // optional\n    this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx;\n    this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 1; // stride at which we apply filters to input volume\n    this.pad = typeof opt.pad !== 'undefined' ? opt.pad : 0; // amount of 0 padding to add around borders of input volume\n    this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0;\n    this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0;\n\n    // computed\n    // note we are doing floor, so if the strided convolution of the filter doesnt fit into the input\n    // volume exactly, the output volume will be trimmed and not contain the (incomplete) computed\n    // final application.\n    this.out_sx = Math.floor((this.in_sx + this.pad * 2 - this.sx) / this.stride + 1);\n    this.out_sy = Math.floor((this.in_sy + this.pad * 2 - this.sy) / this.stride + 1);\n    this.layer_type = 'conv';\n\n    // initializations\n    var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0;\n    this.filters = [];\n    for(var i=0;i<this.out_depth;i++) { this.filters.push(new Vol(this.sx, this.sy, this.in_depth)); }\n    this.biases = new Vol(1, 1, this.out_depth, bias);\n  }\n  ConvLayer.prototype = {\n    forward: function(V, is_training) {\n      // optimized code by @mdda that achieves 2x speedup over previous version\n\n      this.in_act = V;\n      var A = new Vol(this.out_sx |0, this.out_sy |0, this.out_depth |0, 0.0);\n      \n      var V_sx = V.sx |0;\n      var V_sy = V.sy |0;\n      var xy_stride = this.stride |0;\n\n      for(var d=0;d<this.out_depth;d++) {\n        var f = this.filters[d];\n        var x = -this.pad |0;\n        var y = -this.pad |0;\n        for(var ay=0; ay<this.out_sy; y+=xy_stride,ay++) {  // xy_stride\n          x = -this.pad |0;\n          for(var ax=0; ax<this.out_sx; x+=xy_stride,ax++) {  // xy_stride\n\n            // convolve centered at this particular location\n            var a = 0.0;\n            for(var fy=0;fy<f.sy;fy++) {\n              var oy = y+fy; // coordinates in the original input array coordinates\n              for(var fx=0;fx<f.sx;fx++) {\n                var ox = x+fx;\n                if(oy>=0 && oy<V_sy && ox>=0 && ox<V_sx) {\n                  for(var fd=0;fd<f.depth;fd++) {\n                    // avoid function call overhead (x2) for efficiency, compromise modularity :(\n                    a += f.w[((f.sx * fy)+fx)*f.depth+fd] * V.w[((V_sx * oy)+ox)*V.depth+fd];\n                  }\n                }\n              }\n            }\n            a += this.biases.w[d];\n            A.set(ax, ay, d, a);\n          }\n        }\n      }\n      this.out_act = A;\n      return this.out_act;\n    },\n    backward: function() {\n\n      var V = this.in_act;\n      V.dw = global.zeros(V.w.length); // zero out gradient wrt bottom data, we're about to fill it\n\n      var V_sx = V.sx |0;\n      var V_sy = V.sy |0;\n      var xy_stride = this.stride |0;\n\n      for(var d=0;d<this.out_depth;d++) {\n        var f = this.filters[d];\n        var x = -this.pad |0;\n        var y = -this.pad |0;\n        for(var ay=0; ay<this.out_sy; y+=xy_stride,ay++) {  // xy_stride\n          x = -this.pad |0;\n          for(var ax=0; ax<this.out_sx; x+=xy_stride,ax++) {  // xy_stride\n\n            // convolve centered at this particular location\n            var chain_grad = this.out_act.get_grad(ax,ay,d); // gradient from above, from chain rule\n            for(var fy=0;fy<f.sy;fy++) {\n              var oy = y+fy; // coordinates in the original input array coordinates\n              for(var fx=0;fx<f.sx;fx++) {\n                var ox = x+fx;\n                if(oy>=0 && oy<V_sy && ox>=0 && ox<V_sx) {\n                  for(var fd=0;fd<f.depth;fd++) {\n                    // avoid function call overhead (x2) for efficiency, compromise modularity :(\n                    var ix1 = ((V_sx * oy)+ox)*V.depth+fd;\n                    var ix2 = ((f.sx * fy)+fx)*f.depth+fd;\n                    f.dw[ix2] += V.w[ix1]*chain_grad;\n                    V.dw[ix1] += f.w[ix2]*chain_grad;\n                  }\n                }\n              }\n            }\n            this.biases.dw[d] += chain_grad;\n          }\n        }\n      }\n    },\n    getParamsAndGrads: function() {\n      var response = [];\n      for(var i=0;i<this.out_depth;i++) {\n        response.push({params: this.filters[i].w, grads: this.filters[i].dw, l2_decay_mul: this.l2_decay_mul, l1_decay_mul: this.l1_decay_mul});\n      }\n      response.push({params: this.biases.w, grads: this.biases.dw, l1_decay_mul: 0.0, l2_decay_mul: 0.0});\n      return response;\n    },\n    toJSON: function() {\n      var json = {};\n      json.sx = this.sx; // filter size in x, y dims\n      json.sy = this.sy;\n      json.stride = this.stride;\n      json.in_depth = this.in_depth;\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      json.l1_decay_mul = this.l1_decay_mul;\n      json.l2_decay_mul = this.l2_decay_mul;\n      json.pad = this.pad;\n      json.filters = [];\n      for(var i=0;i<this.filters.length;i++) {\n        json.filters.push(this.filters[i].toJSON());\n      }\n      json.biases = this.biases.toJSON();\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type;\n      this.sx = json.sx; // filter size in x, y dims\n      this.sy = json.sy;\n      this.stride = json.stride;\n      this.in_depth = json.in_depth; // depth of input volume\n      this.filters = [];\n      this.l1_decay_mul = typeof json.l1_decay_mul !== 'undefined' ? json.l1_decay_mul : 1.0;\n      this.l2_decay_mul = typeof json.l2_decay_mul !== 'undefined' ? json.l2_decay_mul : 1.0;\n      this.pad = typeof json.pad !== 'undefined' ? json.pad : 0;\n      for(var i=0;i<json.filters.length;i++) {\n        var v = new Vol(0,0,0,0);\n        v.fromJSON(json.filters[i]);\n        this.filters.push(v);\n      }\n      this.biases = new Vol(0,0,0,0);\n      this.biases.fromJSON(json.biases);\n    }\n  }\n\n  var get_deconv_outsize = function(size, k, s, p) {\n    return s * (size - 1) + k - 2 * p;\n  }\n\n  var DeconvLayer = function(opt) {\n    var opt = opt || {};\n\n    // required\n    this.out_depth = opt.filters;\n    this.sx = opt.sx; // filter size. Should be odd if possible, it's cleaner.\n    this.in_depth = opt.in_depth;\n    this.in_sx = opt.in_sx;\n    this.in_sy = opt.in_sy;\n    this.bn = opt.bn;\n\n    // optional\n    this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx;\n    this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 1; // stride at which we apply filters to input volume\n    this.pad = typeof opt.pad !== 'undefined' ? opt.pad : 0; // amount of 0 padding to add around borders of input volume\n    this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0;\n    this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0;\n\n    // computed\n    // note we are doing floor, so if the strided convolution of the filter doesnt fit into the input\n    // volume exactly, the output volume will be trimmed and not contain the (incomplete) computed\n    // final application.\n    if(typeof opt.out_sx !== 'undefined') {\n      this.out_sx = opt.out_sx;\n    } else {\n      this.out_sx = get_deconv_outsize(this.in_sx, this.sx, this.stride, this.pad);\n    }\n    if(typeof opt.out_sy !== 'undefined') {\n      this.out_sy = opt.out_sy;\n    } else {\n      this.out_sy = get_deconv_outsize(this.in_sy, this.sy, this.stride, this.pad);\n    }\n    this.layer_type = 'deconv';\n\n    // initializations\n    var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0;\n    this.filters = [];\n    for(var i=0;i<this.out_depth;i++) { this.filters.push(new Vol(this.sx, this.sy, this.in_depth)); }\n    this.biases = new Vol(1, 1, this.out_depth, bias);\n  }\n  DeconvLayer.prototype = {\n    forward: function(V, is_training) {\n      // optimized code by @mdda that achieves 2x speedup over previous version\n      this.in_act = V;\n      var A = new Vol(this.out_sx |0, this.out_sy |0, this.out_depth |0, 0.0);\n\n      var V_sx = this.in_sx |0;\n      var V_sy = this.in_sy |0;\n      var V_depth = this.in_depth;\n      var xy_stride = this.stride |0;\n\n      for(var d=0;d<this.out_depth;d++) {\n        var f = this.filters[d];\n        for(var ay=0; ay<this.in_sy; y++,ay++) {  // xy_stride\n          var y = (ay * xy_stride - this.pad)|0;\n          for(var ax=0; ax<this.in_sx; x+=xy_stride,ax++) {  // xy_stride\n            var x = (ax * xy_stride - this.pad)|0;\n            // convolve centered at this particular location\n            for(var fy=0;fy<f.sy;fy++) {\n              var iy = y+fy; // coordinates in the original input array coordinates\n              for(var fx=0;fx<f.sx;fx++) {\n                var ix = x+fx;\n                if(iy>=0 && iy<A.sy && ix>=0 && ix<A.sx) {\n                  for(var fd=0;fd<V_depth;fd++) {\n                    var a = f.w[((f.sx * fy)+fx)*V_depth+fd] * V.w[((V_sx * ay)+ax)*V_depth+fd];\n                    if (isNaN(a)) {\n                      a = 1;\n                    }\n                    A.w[((A.sx * iy)+ix)*A.depth + d] += a;\n                  }\n                }\n              }\n            }\n          }\n        }\n        for(var ay=0; ay<this.out_sy; y++,ay++) {  // xy_stride\n          for(var ax=0; ax<this.out_sx; x++,ax++) {  // xy_stride\n            A.w[((A.sx * ay)+ax)*A.depth + d] += this.biases.w[d];\n          }\n        }\n      }\n      if (this.bn) {\n        if (this.out_sy == 1) {\n          var avg = global.average(A.w, 512);\n          var std = global.standardDeviation(A.w, 512);\n\n          for(var i=0;i<this.out_depth;i++) {\n            A.w[i] = ((A.w[i] - avg[i % 512])/std[i % 512]) * this.gamma.w[i % 512] + this.beta.w[i % 512];\n          }\n        }\n        else {\n          var avg = global.average(A.w, this.out_depth);\n          var std = global.standardDeviation(A.w, this.out_depth);\n\n          for(var d=0;d<this.out_depth;d++) {\n            for(var ay=0; ay<this.out_sy; y++,ay++) {  // xy_stride\n              for(var ax=0; ax<this.out_sx; x++,ax++) {  // xy_stride\n                var i = ((A.sx * ay)+ax)*A.depth + d;\n                A.w[i] = ((A.w[i] - avg[d])/std[d])*this.gamma.w[d] + this.beta.w[d];\n              }\n            }\n          }\n        }\n      }\n      this.out_act = A;\n      return this.out_act;\n    },\n    backward: function() {\n      // Not implement.\n    },\n    getParamsAndGrads: function() {\n      var response = [];\n      for(var i=0;i<this.out_depth;i++) {\n        response.push({params: this.filters[i].w, grads: this.filters[i].dw, l2_decay_mul: this.l2_decay_mul, l1_decay_mul: this.l1_decay_mul});\n      }\n      response.push({params: this.biases.w, grads: this.biases.dw, l1_decay_mul: 0.0, l2_decay_mul: 0.0});\n      return response;\n    },\n    toJSON: function() {\n      var json = {};\n      json.sx = this.sx; // filter size in x, y dims\n      json.sy = this.sy;\n      json.stride = this.stride;\n      json.in_depth = this.in_depth;\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      json.l1_decay_mul = this.l1_decay_mul;\n      json.l2_decay_mul = this.l2_decay_mul;\n      json.pad = this.pad;\n      json.filters = [];\n      for(var i=0;i<this.filters.length;i++) {\n        json.filters.push(this.filters[i].toJSON());\n      }\n      json.biases = this.biases.toJSON();\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type;\n      this.sx = json.sx; // filter size in x, y dims\n      this.sy = json.sy;\n      this.stride = json.stride;\n      this.in_depth = json.in_depth; // depth of input volume\n      this.filters = [];\n      this.l1_decay_mul = typeof json.l1_decay_mul !== 'undefined' ? json.l1_decay_mul : 1.0;\n      this.l2_decay_mul = typeof json.l2_decay_mul !== 'undefined' ? json.l2_decay_mul : 1.0;\n      this.pad = typeof json.pad !== 'undefined' ? json.pad : 0;\n      for(var i=0;i<json.filters.length;i++) {\n        var v = new Vol(0,0,0,0);\n        v.fromJSON(json.filters[i]);\n        this.filters.push(v);\n      }\n      this.biases = new Vol(0,0,0,0);\n      this.biases.fromJSON(json.biases);\n      if (this.bn) {\n        this.gamma = new Vol(0,0,0,0);\n        this.gamma.fromJSON(json.gamma);\n        this.beta= new Vol(0,0,0,0);\n        this.beta.fromJSON(json.beta);\n      }\n    }\n  }\n\n  var FullyConnLayer = function(opt) {\n    var opt = opt || {};\n\n    // required\n    // ok fine we will allow 'filters' as the word as well\n    this.out_depth = typeof opt.num_neurons !== 'undefined' ? opt.num_neurons : opt.filters;\n\n    // optional \n    this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0;\n    this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0;\n\n    // computed\n    this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;\n    this.out_sx = 1;\n    this.out_sy = 1;\n    this.layer_type = 'fc';\n\n    // initializations\n    var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0;\n    this.filters = [];\n    for(var i=0;i<this.out_depth ;i++) { this.filters.push(new Vol(1, 1, this.num_inputs)); }\n    this.biases = new Vol(1, 1, this.out_depth, bias);\n  }\n\n  FullyConnLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n      var A = new Vol(1, 1, this.out_depth, 0.0);\n      var Vw = V.w;\n      for(var i=0;i<this.out_depth;i++) {\n        var a = 0.0;\n        var wi = this.filters[i].w;\n        for(var d=0;d<this.num_inputs;d++) {\n          a += Vw[d] * wi[d]; // for efficiency use Vols directly for now\n        }\n        a += this.biases.w[i];\n        A.w[i] = a;\n      }\n      this.out_act = A;\n      return this.out_act;\n    },\n    backward: function() {\n      var V = this.in_act;\n      V.dw = global.zeros(V.w.length); // zero out the gradient in input Vol\n      \n      // compute gradient wrt weights and data\n      for(var i=0;i<this.out_depth;i++) {\n        var tfi = this.filters[i];\n        var chain_grad = this.out_act.dw[i];\n        for(var d=0;d<this.num_inputs;d++) {\n          V.dw[d] += tfi.w[d]*chain_grad; // grad wrt input data\n          tfi.dw[d] += V.w[d]*chain_grad; // grad wrt params\n        }\n        this.biases.dw[i] += chain_grad;\n      }\n    },\n    getParamsAndGrads: function() {\n      var response = [];\n      for(var i=0;i<this.out_depth;i++) {\n        response.push({params: this.filters[i].w, grads: this.filters[i].dw, l1_decay_mul: this.l1_decay_mul, l2_decay_mul: this.l2_decay_mul});\n      }\n      response.push({params: this.biases.w, grads: this.biases.dw, l1_decay_mul: 0.0, l2_decay_mul: 0.0});\n      return response;\n    },\n    toJSON: function() {\n      var json = {};\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      json.num_inputs = this.num_inputs;\n      json.l1_decay_mul = this.l1_decay_mul;\n      json.l2_decay_mul = this.l2_decay_mul;\n      json.filters = [];\n      for(var i=0;i<this.filters.length;i++) {\n        json.filters.push(this.filters[i].toJSON());\n      }\n      json.biases = this.biases.toJSON();\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type;\n      this.num_inputs = json.num_inputs;\n      this.l1_decay_mul = typeof json.l1_decay_mul !== 'undefined' ? json.l1_decay_mul : 1.0;\n      this.l2_decay_mul = typeof json.l2_decay_mul !== 'undefined' ? json.l2_decay_mul : 1.0;\n      this.filters = [];\n      for(var i=0;i<json.filters.length;i++) {\n        var v = new Vol(0,0,0,0);\n        v.fromJSON(json.filters[i]);\n        this.filters.push(v);\n      }\n      this.biases = new Vol(0,0,0,0);\n      this.biases.fromJSON(json.biases);\n    }\n  }\n\n  global.ConvLayer = ConvLayer;\n  global.DeconvLayer = DeconvLayer;\n  global.FullyConnLayer = FullyConnLayer;\n  \n})(convnetjs);\n(function(global) {\n  \"use strict\";\n  var Vol = global.Vol; // convenience\n  \n  var PoolLayer = function(opt) {\n\n    var opt = opt || {};\n\n    // required\n    this.sx = opt.sx; // filter size\n    this.in_depth = opt.in_depth;\n    this.in_sx = opt.in_sx;\n    this.in_sy = opt.in_sy;\n\n    // optional\n    this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx;\n    this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 2;\n    this.pad = typeof opt.pad !== 'undefined' ? opt.pad : 0; // amount of 0 padding to add around borders of input volume\n\n    // computed\n    this.out_depth = this.in_depth;\n    this.out_sx = Math.floor((this.in_sx + this.pad * 2 - this.sx) / this.stride + 1);\n    this.out_sy = Math.floor((this.in_sy + this.pad * 2 - this.sy) / this.stride + 1);\n    this.layer_type = 'pool';\n    // store switches for x,y coordinates for where the max comes from, for each output neuron\n    this.switchx = global.zeros(this.out_sx*this.out_sy*this.out_depth);\n    this.switchy = global.zeros(this.out_sx*this.out_sy*this.out_depth);\n  }\n\n  PoolLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n\n      var A = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0);\n      \n      var n=0; // a counter for switches\n      for(var d=0;d<this.out_depth;d++) {\n        var x = -this.pad;\n        var y = -this.pad;\n        for(var ax=0; ax<this.out_sx; x+=this.stride,ax++) {\n          y = -this.pad;\n          for(var ay=0; ay<this.out_sy; y+=this.stride,ay++) {\n\n            // convolve centered at this particular location\n            var a = -99999; // hopefully small enough ;\\\n            var winx=-1,winy=-1;\n            for(var fx=0;fx<this.sx;fx++) {\n              for(var fy=0;fy<this.sy;fy++) {\n                var oy = y+fy;\n                var ox = x+fx;\n                if(oy>=0 && oy<V.sy && ox>=0 && ox<V.sx) {\n                  var v = V.get(ox, oy, d);\n                  // perform max pooling and store pointers to where\n                  // the max came from. This will speed up backprop \n                  // and can help make nice visualizations in future\n                  if(v > a) { a = v; winx=ox; winy=oy;}\n                }\n              }\n            }\n            this.switchx[n] = winx;\n            this.switchy[n] = winy;\n            n++;\n            A.set(ax, ay, d, a);\n          }\n        }\n      }\n      this.out_act = A;\n      return this.out_act;\n    },\n    backward: function() { \n      // pooling layers have no parameters, so simply compute \n      // gradient wrt data here\n      var V = this.in_act;\n      V.dw = global.zeros(V.w.length); // zero out gradient wrt data\n      var A = this.out_act; // computed in forward pass \n\n      var n = 0;\n      for(var d=0;d<this.out_depth;d++) {\n        var x = -this.pad;\n        var y = -this.pad;\n        for(var ax=0; ax<this.out_sx; x+=this.stride,ax++) {\n          y = -this.pad;\n          for(var ay=0; ay<this.out_sy; y+=this.stride,ay++) {\n\n            var chain_grad = this.out_act.get_grad(ax,ay,d);\n            V.add_grad(this.switchx[n], this.switchy[n], d, chain_grad);\n            n++;\n\n          }\n        }\n      }\n    },\n    getParamsAndGrads: function() {\n      return [];\n    },\n    toJSON: function() {\n      var json = {};\n      json.sx = this.sx;\n      json.sy = this.sy;\n      json.stride = this.stride;\n      json.in_depth = this.in_depth;\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      json.pad = this.pad;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type;\n      this.sx = json.sx;\n      this.sy = json.sy;\n      this.stride = json.stride;\n      this.in_depth = json.in_depth;\n      this.pad = typeof json.pad !== 'undefined' ? json.pad : 0; // backwards compatibility\n      this.switchx = global.zeros(this.out_sx*this.out_sy*this.out_depth); // need to re-init these appropriately\n      this.switchy = global.zeros(this.out_sx*this.out_sy*this.out_depth);\n    }\n  }\n\n  global.PoolLayer = PoolLayer;\n\n})(convnetjs);\n\n(function(global) {\n  \"use strict\";\n  var Vol = global.Vol; // convenience\n  var getopt = global.getopt;\n\n  var InputLayer = function(opt) {\n    var opt = opt || {};\n\n    // required: depth\n    this.out_depth = getopt(opt, ['out_depth', 'depth'], 0);\n\n    // optional: default these dimensions to 1\n    this.out_sx = getopt(opt, ['out_sx', 'sx', 'width'], 1);\n    this.out_sy = getopt(opt, ['out_sy', 'sy', 'height'], 1);\n    \n    // computed\n    this.layer_type = 'input';\n  }\n  InputLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n      this.out_act = V;\n      return this.out_act; // simply identity function for now\n    },\n    backward: function() { },\n    getParamsAndGrads: function() {\n      return [];\n    },\n    toJSON: function() {\n      var json = {};\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type; \n    }\n  }\n\n  global.InputLayer = InputLayer;\n})(convnetjs);\n(function(global) {\n  \"use strict\";\n  var Vol = global.Vol; // convenience\n  \n  // Layers that implement a loss. Currently these are the layers that \n  // can initiate a backward() pass. In future we probably want a more \n  // flexible system that can accomodate multiple losses to do multi-task\n  // learning, and stuff like that. But for now, one of the layers in this\n  // file must be the final layer in a Net.\n\n  // This is a classifier, with N discrete classes from 0 to N-1\n  // it gets a stream of N incoming numbers and computes the softmax\n  // function (exponentiate and normalize to sum to 1 as probabilities should)\n  var SoftmaxLayer = function(opt) {\n    var opt = opt || {};\n\n    // computed\n    this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;\n    this.out_depth = this.num_inputs;\n    this.out_sx = 1;\n    this.out_sy = 1;\n    this.layer_type = 'softmax';\n  }\n\n  SoftmaxLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n\n      var A = new Vol(1, 1, this.out_depth, 0.0);\n\n      // compute max activation\n      var as = V.w;\n      var amax = V.w[0];\n      for(var i=1;i<this.out_depth;i++) {\n        if(as[i] > amax) amax = as[i];\n      }\n\n      // compute exponentials (carefully to not blow up)\n      var es = global.zeros(this.out_depth);\n      var esum = 0.0;\n      for(var i=0;i<this.out_depth;i++) {\n        var e = Math.exp(as[i] - amax);\n        esum += e;\n        es[i] = e;\n      }\n\n      // normalize and output to sum to one\n      for(var i=0;i<this.out_depth;i++) {\n        es[i] /= esum;\n        A.w[i] = es[i];\n      }\n\n      this.es = es; // save these for backprop\n      this.out_act = A;\n      return this.out_act;\n    },\n    backward: function(y) {\n\n      // compute and accumulate gradient wrt weights and bias of this layer\n      var x = this.in_act;\n      x.dw = global.zeros(x.w.length); // zero out the gradient of input Vol\n\n      for(var i=0;i<this.out_depth;i++) {\n        var indicator = i === y ? 1.0 : 0.0;\n        var mul = -(indicator - this.es[i]);\n        x.dw[i] = mul;\n      }\n\n      // loss is the class negative log likelihood\n      return -Math.log(this.es[y]);\n    },\n    getParamsAndGrads: function() { \n      return [];\n    },\n    toJSON: function() {\n      var json = {};\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      json.num_inputs = this.num_inputs;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type;\n      this.num_inputs = json.num_inputs;\n    }\n  }\n\n  // implements an L2 regression cost layer,\n  // so penalizes \\sum_i(||x_i - y_i||^2), where x is its input\n  // and y is the user-provided array of \"correct\" values.\n  var RegressionLayer = function(opt) {\n    var opt = opt || {};\n\n    // computed\n    this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;\n    this.out_depth = this.num_inputs;\n    this.out_sx = 1;\n    this.out_sy = 1;\n    this.layer_type = 'regression';\n  }\n\n  RegressionLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n      this.out_act = V;\n      return V; // identity function\n    },\n    // y is a list here of size num_inputs\n    // or it can be a number if only one value is regressed\n    // or it can be a struct {dim: i, val: x} where we only want to \n    // regress on dimension i and asking it to have value x\n    backward: function(y) { \n\n      // compute and accumulate gradient wrt weights and bias of this layer\n      var x = this.in_act;\n      x.dw = global.zeros(x.w.length); // zero out the gradient of input Vol\n      var loss = 0.0;\n      if(y instanceof Array || y instanceof Float64Array) {\n        for(var i=0;i<this.out_depth;i++) {\n          var dy = x.w[i] - y[i];\n          x.dw[i] = dy;\n          loss += 0.5*dy*dy;\n        }\n      } else if(typeof y === 'number') {\n        // lets hope that only one number is being regressed\n        var dy = x.w[0] - y;\n        x.dw[0] = dy;\n        loss += 0.5*dy*dy;\n      } else {\n        // assume it is a struct with entries .dim and .val\n        // and we pass gradient only along dimension dim to be equal to val\n        var i = y.dim;\n        var yi = y.val;\n        var dy = x.w[i] - yi;\n        x.dw[i] = dy;\n        loss += 0.5*dy*dy;\n      }\n      return loss;\n    },\n    getParamsAndGrads: function() { \n      return [];\n    },\n    toJSON: function() {\n      var json = {};\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      json.num_inputs = this.num_inputs;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type;\n      this.num_inputs = json.num_inputs;\n    }\n  }\n\n  var SVMLayer = function(opt) {\n    var opt = opt || {};\n\n    // computed\n    this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;\n    this.out_depth = this.num_inputs;\n    this.out_sx = 1;\n    this.out_sy = 1;\n    this.layer_type = 'svm';\n  }\n\n  SVMLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n      this.out_act = V; // nothing to do, output raw scores\n      return V;\n    },\n    backward: function(y) {\n\n      // compute and accumulate gradient wrt weights and bias of this layer\n      var x = this.in_act;\n      x.dw = global.zeros(x.w.length); // zero out the gradient of input Vol\n\n      // we're using structured loss here, which means that the score\n      // of the ground truth should be higher than the score of any other \n      // class, by a margin\n      var yscore = x.w[y]; // score of ground truth\n      var margin = 1.0;\n      var loss = 0.0;\n      for(var i=0;i<this.out_depth;i++) {\n        if(y === i) { continue; }\n        var ydiff = -yscore + x.w[i] + margin;\n        if(ydiff > 0) {\n          // violating dimension, apply loss\n          x.dw[i] += 1;\n          x.dw[y] -= 1;\n          loss += ydiff;\n        }\n      }\n\n      return loss;\n    },\n    getParamsAndGrads: function() { \n      return [];\n    },\n    toJSON: function() {\n      var json = {};\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      json.num_inputs = this.num_inputs;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type;\n      this.num_inputs = json.num_inputs;\n    }\n  }\n  \n  global.RegressionLayer = RegressionLayer;\n  global.SoftmaxLayer = SoftmaxLayer;\n  global.SVMLayer = SVMLayer;\n\n})(convnetjs);\n\n(function(global) {\n  \"use strict\";\n  var Vol = global.Vol; // convenience\n  \n  // Implements ReLU nonlinearity elementwise\n  // x -> max(0, x)\n  // the output is in [0, inf)\n  var ReluLayer = function(opt) {\n    var opt = opt || {};\n\n    // computed\n    this.out_sx = opt.in_sx;\n    this.out_sy = opt.in_sy;\n    this.out_depth = opt.in_depth;\n    this.layer_type = 'relu';\n  }\n  ReluLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n      var V2 = V.clone();\n      var N = V.w.length;\n      var V2w = V2.w;\n      for(var i=0;i<N;i++) { \n        if(V2w[i] < 0) V2w[i] = 0; // threshold at 0\n      }\n      this.out_act = V2;\n      return this.out_act;\n    },\n    backward: function() {\n      var V = this.in_act; // we need to set dw of this\n      var V2 = this.out_act;\n      var N = V.w.length;\n      V.dw = global.zeros(N); // zero out gradient wrt data\n      for(var i=0;i<N;i++) {\n        if(V2.w[i] <= 0) V.dw[i] = 0; // threshold\n        else V.dw[i] = V2.dw[i];\n      }\n    },\n    getParamsAndGrads: function() {\n      return [];\n    },\n    toJSON: function() {\n      var json = {};\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type; \n    }\n  }\n\n  // Implements Sigmoid nnonlinearity elementwise\n  // x -> 1/(1+e^(-x))\n  // so the output is between 0 and 1.\n  var SigmoidLayer = function(opt) {\n    var opt = opt || {};\n\n    // computed\n    this.out_sx = opt.in_sx;\n    this.out_sy = opt.in_sy;\n    this.out_depth = opt.in_depth;\n    this.layer_type = 'sigmoid';\n  }\n  SigmoidLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n      var V2 = V.cloneAndZero();\n      var N = V.w.length;\n      var V2w = V2.w;\n      var Vw = V.w;\n      for(var i=0;i<N;i++) { \n        V2w[i] = 1.0/(1.0+Math.exp(-Vw[i]));\n      }\n      this.out_act = V2;\n      return this.out_act;\n    },\n    backward: function() {\n      var V = this.in_act; // we need to set dw of this\n      var V2 = this.out_act;\n      var N = V.w.length;\n      V.dw = global.zeros(N); // zero out gradient wrt data\n      for(var i=0;i<N;i++) {\n        var v2wi = V2.w[i];\n        V.dw[i] =  v2wi * (1.0 - v2wi) * V2.dw[i];\n      }\n    },\n    getParamsAndGrads: function() {\n      return [];\n    },\n    toJSON: function() {\n      var json = {};\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type; \n    }\n  }\n\n  // Implements Maxout nnonlinearity that computes\n  // x -> max(x)\n  // where x is a vector of size group_size. Ideally of course,\n  // the input size should be exactly divisible by group_size\n  var MaxoutLayer = function(opt) {\n    var opt = opt || {};\n\n    // required\n    this.group_size = typeof opt.group_size !== 'undefined' ? opt.group_size : 2;\n\n    // computed\n    this.out_sx = opt.in_sx;\n    this.out_sy = opt.in_sy;\n    this.out_depth = Math.floor(opt.in_depth / this.group_size);\n    this.layer_type = 'maxout';\n\n    this.switches = global.zeros(this.out_sx*this.out_sy*this.out_depth); // useful for backprop\n  }\n  MaxoutLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n      var N = this.out_depth; \n      var V2 = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0);\n\n      // optimization branch. If we're operating on 1D arrays we dont have\n      // to worry about keeping track of x,y,d coordinates inside\n      // input volumes. In convnets we do :(\n      if(this.out_sx === 1 && this.out_sy === 1) {\n        for(var i=0;i<N;i++) {\n          var ix = i * this.group_size; // base index offset\n          var a = V.w[ix];\n          var ai = 0;\n          for(var j=1;j<this.group_size;j++) {\n            var a2 = V.w[ix+j];\n            if(a2 > a) {\n              a = a2;\n              ai = j;\n            }\n          }\n          V2.w[i] = a;\n          this.switches[i] = ix + ai;\n        }\n      } else {\n        var n=0; // counter for switches\n        for(var x=0;x<V.sx;x++) {\n          for(var y=0;y<V.sy;y++) {\n            for(var i=0;i<N;i++) {\n              var ix = i * this.group_size;\n              var a = V.get(x, y, ix);\n              var ai = 0;\n              for(var j=1;j<this.group_size;j++) {\n                var a2 = V.get(x, y, ix+j);\n                if(a2 > a) {\n                  a = a2;\n                  ai = j;\n                }\n              }\n              V2.set(x,y,i,a);\n              this.switches[n] = ix + ai;\n              n++;\n            }\n          }\n        }\n\n      }\n      this.out_act = V2;\n      return this.out_act;\n    },\n    backward: function() {\n      var V = this.in_act; // we need to set dw of this\n      var V2 = this.out_act;\n      var N = this.out_depth;\n      V.dw = global.zeros(V.w.length); // zero out gradient wrt data\n\n      // pass the gradient through the appropriate switch\n      if(this.out_sx === 1 && this.out_sy === 1) {\n        for(var i=0;i<N;i++) {\n          var chain_grad = V2.dw[i];\n          V.dw[this.switches[i]] = chain_grad;\n        }\n      } else {\n        // bleh okay, lets do this the hard way\n        var n=0; // counter for switches\n        for(var x=0;x<V2.sx;x++) {\n          for(var y=0;y<V2.sy;y++) {\n            for(var i=0;i<N;i++) {\n              var chain_grad = V2.get_grad(x,y,i);\n              V.set_grad(x,y,this.switches[n],chain_grad);\n              n++;\n            }\n          }\n        }\n      }\n    },\n    getParamsAndGrads: function() {\n      return [];\n    },\n    toJSON: function() {\n      var json = {};\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      json.group_size = this.group_size;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type; \n      this.group_size = json.group_size;\n      this.switches = global.zeros(this.group_size);\n    }\n  }\n\n  // a helper function, since tanh is not yet part of ECMAScript. Will be in v6.\n  function tanh(x) {\n    var y = Math.exp(2 * x);\n    return (y - 1) / (y + 1);\n  }\n  // Implements Tanh nnonlinearity elementwise\n  // x -> tanh(x) \n  // so the output is between -1 and 1.\n  var TanhLayer = function(opt) {\n    var opt = opt || {};\n\n    // computed\n    this.out_sx = opt.in_sx;\n    this.out_sy = opt.in_sy;\n    this.out_depth = opt.in_depth;\n    this.layer_type = 'tanh';\n  }\n  TanhLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n      var V2 = V.cloneAndZero();\n      var N = V.w.length;\n      for(var i=0;i<N;i++) { \n        V2.w[i] = tanh(V.w[i]);\n      }\n      this.out_act = V2;\n      return this.out_act;\n    },\n    backward: function() {\n      var V = this.in_act; // we need to set dw of this\n      var V2 = this.out_act;\n      var N = V.w.length;\n      V.dw = global.zeros(N); // zero out gradient wrt data\n      for(var i=0;i<N;i++) {\n        var v2wi = V2.w[i];\n        V.dw[i] = (1.0 - v2wi * v2wi) * V2.dw[i];\n      }\n    },\n    getParamsAndGrads: function() {\n      return [];\n    },\n    toJSON: function() {\n      var json = {};\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type; \n    }\n  }\n  \n  global.TanhLayer = TanhLayer;\n  global.MaxoutLayer = MaxoutLayer;\n  global.ReluLayer = ReluLayer;\n  global.SigmoidLayer = SigmoidLayer;\n\n})(convnetjs);\n\n(function(global) {\n  \"use strict\";\n  var Vol = global.Vol; // convenience\n\n  // An inefficient dropout layer\n  // Note this is not most efficient implementation since the layer before\n  // computed all these activations and now we're just going to drop them :(\n  // same goes for backward pass. Also, if we wanted to be efficient at test time\n  // we could equivalently be clever and upscale during train and copy pointers during test\n  // todo: make more efficient.\n  var DropoutLayer = function(opt) {\n    var opt = opt || {};\n\n    // computed\n    this.out_sx = opt.in_sx;\n    this.out_sy = opt.in_sy;\n    this.out_depth = opt.in_depth;\n    this.layer_type = 'dropout';\n    this.drop_prob = typeof opt.drop_prob !== 'undefined' ? opt.drop_prob : 0.5;\n    this.dropped = global.zeros(this.out_sx*this.out_sy*this.out_depth);\n  }\n  DropoutLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n      if(typeof(is_training)==='undefined') { is_training = false; } // default is prediction mode\n      var V2 = V.clone();\n      var N = V.w.length;\n      if(is_training) {\n        // do dropout\n        for(var i=0;i<N;i++) {\n          if(Math.random()<this.drop_prob) { V2.w[i]=0; this.dropped[i] = true; } // drop!\n          else {this.dropped[i] = false;}\n        }\n      } else {\n        // scale the activations during prediction\n        for(var i=0;i<N;i++) { V2.w[i]*=this.drop_prob; }\n      }\n      this.out_act = V2;\n      return this.out_act; // dummy identity function for now\n    },\n    backward: function() {\n      var V = this.in_act; // we need to set dw of this\n      var chain_grad = this.out_act;\n      var N = V.w.length;\n      V.dw = global.zeros(N); // zero out gradient wrt data\n      for(var i=0;i<N;i++) {\n        if(!(this.dropped[i])) { \n          V.dw[i] = chain_grad.dw[i]; // copy over the gradient\n        }\n      }\n    },\n    getParamsAndGrads: function() {\n      return [];\n    },\n    toJSON: function() {\n      var json = {};\n      json.out_depth = this.out_depth;\n      json.out_sx = this.out_sx;\n      json.out_sy = this.out_sy;\n      json.layer_type = this.layer_type;\n      json.drop_prob = this.drop_prob;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.out_depth = json.out_depth;\n      this.out_sx = json.out_sx;\n      this.out_sy = json.out_sy;\n      this.layer_type = json.layer_type; \n      this.drop_prob = json.drop_prob;\n    }\n  }\n  \n\n  global.DropoutLayer = DropoutLayer;\n})(convnetjs);\n(function(global) {\n  \"use strict\";\n  var Vol = global.Vol; // convenience\n  \n  // a bit experimental layer for now. I think it works but I'm not 100%\n  // the gradient check is a bit funky. I'll look into this a bit later.\n  // Local Response Normalization in window, along depths of volumes\n  var LocalResponseNormalizationLayer = function(opt) {\n    var opt = opt || {};\n\n    // required\n    this.k = opt.k;\n    this.n = opt.n;\n    this.alpha = opt.alpha;\n    this.beta = opt.beta;\n\n    // computed\n    this.out_sx = opt.in_sx;\n    this.out_sy = opt.in_sy;\n    this.out_depth = opt.in_depth;\n    this.layer_type = 'lrn';\n\n    // checks\n    if(this.n%2 === 0) { console.log('WARNING n should be odd for LRN layer'); }\n  }\n  LocalResponseNormalizationLayer.prototype = {\n    forward: function(V, is_training) {\n      this.in_act = V;\n\n      var A = V.cloneAndZero();\n      this.S_cache_ = V.cloneAndZero();\n      var n2 = Math.floor(this.n/2);\n      for(var x=0;x<V.sx;x++) {\n        for(var y=0;y<V.sy;y++) {\n          for(var i=0;i<V.depth;i++) {\n\n            var ai = V.get(x,y,i);\n\n            // normalize in a window of size n\n            var den = 0.0;\n            for(var j=Math.max(0,i-n2);j<=Math.min(i+n2,V.depth-1);j++) {\n              var aa = V.get(x,y,j);\n              den += aa*aa;\n            }\n            den *= this.alpha / this.n;\n            den += this.k;\n            this.S_cache_.set(x,y,i,den); // will be useful for backprop\n            den = Math.pow(den, this.beta);\n            A.set(x,y,i,ai/den);\n          }\n        }\n      }\n\n      this.out_act = A;\n      return this.out_act; // dummy identity function for now\n    },\n    backward: function() { \n      // evaluate gradient wrt data\n      var V = this.in_act; // we need to set dw of this\n      V.dw = global.zeros(V.w.length); // zero out gradient wrt data\n      var A = this.out_act; // computed in forward pass \n\n      var n2 = Math.floor(this.n/2);\n      for(var x=0;x<V.sx;x++) {\n        for(var y=0;y<V.sy;y++) {\n          for(var i=0;i<V.depth;i++) {\n\n            var chain_grad = this.out_act.get_grad(x,y,i);\n            var S = this.S_cache_.get(x,y,i);\n            var SB = Math.pow(S, this.beta);\n            var SB2 = SB*SB;\n\n            // normalize in a window of size n\n            for(var j=Math.max(0,i-n2);j<=Math.min(i+n2,V.depth-1);j++) {              \n              var aj = V.get(x,y,j); \n              var g = -aj*this.beta*Math.pow(S,this.beta-1)*this.alpha/this.n*2*aj;\n              if(j===i) g+= SB;\n              g /= SB2;\n              g *= chain_grad;\n              V.add_grad(x,y,j,g);\n            }\n\n          }\n        }\n      }\n    },\n    getParamsAndGrads: function() { return []; },\n    toJSON: function() {\n      var json = {};\n      json.k = this.k;\n      json.n = this.n;\n      json.alpha = this.alpha; // normalize by size\n      json.beta = this.beta;\n      json.out_sx = this.out_sx; \n      json.out_sy = this.out_sy;\n      json.out_depth = this.out_depth;\n      json.layer_type = this.layer_type;\n      return json;\n    },\n    fromJSON: function(json) {\n      this.k = json.k;\n      this.n = json.n;\n      this.alpha = json.alpha; // normalize by size\n      this.beta = json.beta;\n      this.out_sx = json.out_sx; \n      this.out_sy = json.out_sy;\n      this.out_depth = json.out_depth;\n      this.layer_type = json.layer_type;\n    }\n  }\n  \n\n  global.LocalResponseNormalizationLayer = LocalResponseNormalizationLayer;\n})(convnetjs);\n(function(global) {\n  \"use strict\";\n  var Vol = global.Vol; // convenience\n  var assert = global.assert;\n\n  // Net manages a set of layers\n  // For now constraints: Simple linear order of layers, first layer input last layer a cost layer\n  var Net = function(options) {\n    this.layers = [];\n  }\n\n  Net.prototype = {\n    \n    // takes a list of layer definitions and creates the network layer objects\n    makeLayers: function(defs) {\n\n      // few checks\n      assert(defs.length >= 2, 'Error! At least one input layer and one loss layer are required.');\n      assert(defs[0].type === 'input', 'Error! First layer must be the input layer, to declare size of inputs');\n\n      // desugar layer_defs for adding activation, dropout layers etc\n      var desugar = function() {\n        var new_defs = [];\n        for(var i=0;i<defs.length;i++) {\n          var def = defs[i];\n          \n          if(def.type==='softmax' || def.type==='svm') {\n            // add an fc layer here, there is no reason the user should\n            // have to worry about this and we almost always want to\n            new_defs.push({type:'fc', num_neurons: def.num_classes});\n          }\n\n          if(def.type==='regression') {\n            // add an fc layer here, there is no reason the user should\n            // have to worry about this and we almost always want to\n            new_defs.push({type:'fc', num_neurons: def.num_neurons});\n          }\n\n          if((def.type==='fc' || def.type==='conv') \n              && typeof(def.bias_pref) === 'undefined'){\n            def.bias_pref = 0.0;\n            if(typeof def.activation !== 'undefined' && def.activation === 'relu') {\n              def.bias_pref = 0.1; // relus like a bit of positive bias to get gradients early\n              // otherwise it's technically possible that a relu unit will never turn on (by chance)\n              // and will never get any gradient and never contribute any computation. Dead relu.\n            }\n          }\n\n          new_defs.push(def);\n\n          if(typeof def.activation !== 'undefined') {\n            if(def.activation==='relu') { new_defs.push({type:'relu'}); }\n            else if (def.activation==='sigmoid') { new_defs.push({type:'sigmoid'}); }\n            else if (def.activation==='tanh') { new_defs.push({type:'tanh'}); }\n            else if (def.activation==='maxout') {\n              // create maxout activation, and pass along group size, if provided\n              var gs = def.group_size !== 'undefined' ? def.group_size : 2;\n              new_defs.push({type:'maxout', group_size:gs});\n            }\n            else { console.log('ERROR unsupported activation ' + def.activation); }\n          }\n          if(typeof def.drop_prob !== 'undefined' && def.type !== 'dropout') {\n            new_defs.push({type:'dropout', drop_prob: def.drop_prob});\n          }\n\n        }\n        return new_defs;\n      }\n      defs = desugar(defs);\n\n      // create the layers\n      this.layers = [];\n      for(var i=0;i<defs.length;i++) {\n        var def = defs[i];\n        if(i>0) {\n          var prev = this.layers[i-1];\n          def.in_sx = prev.out_sx;\n          def.in_sy = prev.out_sy;\n          def.in_depth = prev.out_depth;\n        }\n\n        switch(def.type) {\n          case 'fc': this.layers.push(new global.FullyConnLayer(def)); break;\n          case 'lrn': this.layers.push(new global.LocalResponseNormalizationLayer(def)); break;\n          case 'dropout': this.layers.push(new global.DropoutLayer(def)); break;\n          case 'input': this.layers.push(new global.InputLayer(def)); break;\n          case 'softmax': this.layers.push(new global.SoftmaxLayer(def)); break;\n          case 'regression': this.layers.push(new global.RegressionLayer(def)); break;\n          case 'conv': this.layers.push(new global.ConvLayer(def)); break;\n          case 'deconv': this.layers.push(new global.DeconvLayer(def)); break;\n          case 'pool': this.layers.push(new global.PoolLayer(def)); break;\n          case 'relu': this.layers.push(new global.ReluLayer(def)); break;\n          case 'sigmoid': this.layers.push(new global.SigmoidLayer(def)); break;\n          case 'tanh': this.layers.push(new global.TanhLayer(def)); break;\n          case 'maxout': this.layers.push(new global.MaxoutLayer(def)); break;\n          case 'svm': this.layers.push(new global.SVMLayer(def)); break;\n          default: console.log('ERROR: UNRECOGNIZED LAYER TYPE: ' + def.type);\n        }\n      }\n    },\n\n    // forward prop the network. \n    // The trainer class passes is_training = true, but when this function is\n    // called from outside (not from the trainer), it defaults to prediction mode\n    forward: function(V, is_training) {\n      if(typeof(is_training) === 'undefined') is_training = false;\n      var act = this.layers[0].forward(V, is_training);\n      for(var i=1;i<this.layers.length;i++) {\n        act = this.layers[i].forward(act, is_training);\n      }\n      return act;\n    },\n\n    getCostLoss: function(V, y) {\n      this.forward(V, false);\n      var N = this.layers.length;\n      var loss = this.layers[N-1].backward(y);\n      return loss;\n    },\n    \n    // backprop: compute gradients wrt all parameters\n    backward: function(y) {\n      var N = this.layers.length;\n      var loss = this.layers[N-1].backward(y); // last layer assumed to be loss layer\n      for(var i=N-2;i>=0;i--) { // first layer assumed input\n        this.layers[i].backward();\n      }\n      return loss;\n    },\n    getParamsAndGrads: function() {\n      // accumulate parameters and gradients for the entire network\n      var response = [];\n      for(var i=0;i<this.layers.length;i++) {\n        var layer_reponse = this.layers[i].getParamsAndGrads();\n        for(var j=0;j<layer_reponse.length;j++) {\n          response.push(layer_reponse[j]);\n        }\n      }\n      return response;\n    },\n    getPrediction: function() {\n      // this is a convenience function for returning the argmax\n      // prediction, assuming the last layer of the net is a softmax\n      var S = this.layers[this.layers.length-1];\n      assert(S.layer_type === 'softmax', 'getPrediction function assumes softmax as last layer of the net!');\n\n      var p = S.out_act.w;\n      var maxv = p[0];\n      var maxi = 0;\n      for(var i=1;i<p.length;i++) {\n        if(p[i] > maxv) { maxv = p[i]; maxi = i;}\n      }\n      return maxi; // return index of the class with highest class probability\n    },\n    toJSON: function() {\n      var json = {};\n      json.layers = [];\n      for(var i=0;i<this.layers.length;i++) {\n        json.layers.push(this.layers[i].toJSON());\n      }\n      return json;\n    },\n    fromJSON: function(json) {\n      this.layers = [];\n      for(var i=0;i<json.layers.length;i++) {\n        var Lj = json.layers[i]\n        var t = Lj.layer_type;\n        var L;\n        if(t==='input') { L = new global.InputLayer(); }\n        if(t==='relu') { L = new global.ReluLayer(); }\n        if(t==='sigmoid') { L = new global.SigmoidLayer(); }\n        if(t==='tanh') { L = new global.TanhLayer(); }\n        if(t==='dropout') { L = new global.DropoutLayer(); }\n        if(t==='conv') { L = new global.ConvLayer(); }\n        if(t==='pool') { L = new global.PoolLayer(); }\n        if(t==='lrn') { L = new global.LocalResponseNormalizationLayer(); }\n        if(t==='softmax') { L = new global.SoftmaxLayer(); }\n        if(t==='regression') { L = new global.RegressionLayer(); }\n        if(t==='fc') { L = new global.FullyConnLayer(); }\n        if(t==='maxout') { L = new global.MaxoutLayer(); }\n        if(t==='svm') { L = new global.SVMLayer(); }\n        L.fromJSON(Lj);\n        this.layers.push(L);\n      }\n    }\n  }\n  \n  global.Net = Net;\n})(convnetjs);\n(function(global) {\n  \"use strict\";\n  var Vol = global.Vol; // convenience\n\n  var Trainer = function(net, options) {\n\n    this.net = net;\n\n    var options = options || {};\n    this.learning_rate = typeof options.learning_rate !== 'undefined' ? options.learning_rate : 0.01;\n    this.l1_decay = typeof options.l1_decay !== 'undefined' ? options.l1_decay : 0.0;\n    this.l2_decay = typeof options.l2_decay !== 'undefined' ? options.l2_decay : 0.0;\n    this.batch_size = typeof options.batch_size !== 'undefined' ? options.batch_size : 1;\n    this.method = typeof options.method !== 'undefined' ? options.method : 'sgd'; // sgd/adam/adagrad/adadelta/windowgrad/netsterov\n\n    this.momentum = typeof options.momentum !== 'undefined' ? options.momentum : 0.9;\n    this.ro = typeof options.ro !== 'undefined' ? options.ro : 0.95; // used in adadelta\n    this.eps = typeof options.eps !== 'undefined' ? options.eps : 1e-8; // used in adam or adadelta\n    this.beta1 = typeof options.beta1 !== 'undefined' ? options.beta1 : 0.9; // used in adam\n    this.beta2 = typeof options.beta2 !== 'undefined' ? options.beta2 : 0.999; // used in adam\n\n    this.k = 0; // iteration counter\n    this.gsum = []; // last iteration gradients (used for momentum calculations)\n    this.xsum = []; // used in adam or adadelta\n\n    // check if regression is expected \n    if(this.net.layers[this.net.layers.length - 1].layer_type === \"regression\")\n      this.regression = true;\n    else\n      this.regression = false;\n  }\n\n  Trainer.prototype = {\n    train: function(x, y) {\n\n      var start = new Date().getTime();\n      this.net.forward(x, true); // also set the flag that lets the net know we're just training\n      var end = new Date().getTime();\n      var fwd_time = end - start;\n\n      var start = new Date().getTime();\n      var cost_loss = this.net.backward(y);\n      var l2_decay_loss = 0.0;\n      var l1_decay_loss = 0.0;\n      var end = new Date().getTime();\n      var bwd_time = end - start;\n\n      if(this.regression && y.constructor !== Array)\n        console.log(\"Warning: a regression net requires an array as training output vector.\");\n      \n      this.k++;\n      if(this.k % this.batch_size === 0) {\n\n        var pglist = this.net.getParamsAndGrads();\n\n        // initialize lists for accumulators. Will only be done once on first iteration\n        if(this.gsum.length === 0 && (this.method !== 'sgd' || this.momentum > 0.0)) {\n          // only vanilla sgd doesnt need either lists\n          // momentum needs gsum\n          // adagrad needs gsum\n          // adam and adadelta needs gsum and xsum\n          for(var i=0;i<pglist.length;i++) {\n            this.gsum.push(global.zeros(pglist[i].params.length));\n            if(this.method === 'adam' || this.method === 'adadelta') {\n              this.xsum.push(global.zeros(pglist[i].params.length));\n            } else {\n              this.xsum.push([]); // conserve memory\n            }\n          }\n        }\n\n        // perform an update for all sets of weights\n        for(var i=0;i<pglist.length;i++) {\n          var pg = pglist[i]; // param, gradient, other options in future (custom learning rate etc)\n          var p = pg.params;\n          var g = pg.grads;\n\n          // learning rate for some parameters.\n          var l2_decay_mul = typeof pg.l2_decay_mul !== 'undefined' ? pg.l2_decay_mul : 1.0;\n          var l1_decay_mul = typeof pg.l1_decay_mul !== 'undefined' ? pg.l1_decay_mul : 1.0;\n          var l2_decay = this.l2_decay * l2_decay_mul;\n          var l1_decay = this.l1_decay * l1_decay_mul;\n\n          var plen = p.length;\n          for(var j=0;j<plen;j++) {\n            l2_decay_loss += l2_decay*p[j]*p[j]/2; // accumulate weight decay loss\n            l1_decay_loss += l1_decay*Math.abs(p[j]);\n            var l1grad = l1_decay * (p[j] > 0 ? 1 : -1);\n            var l2grad = l2_decay * (p[j]);\n\n            var gij = (l2grad + l1grad + g[j]) / this.batch_size; // raw batch gradient\n\n            var gsumi = this.gsum[i];\n            var xsumi = this.xsum[i];\n            if(this.method === 'adam') {\n              // adam update\n              gsumi[j] = gsumi[j] * this.beta1 + (1- this.beta1) * gij; // update biased first moment estimate\n              xsumi[j] = xsumi[j] * this.beta2 + (1-this.beta2) * gij * gij; // update biased second moment estimate\n              var biasCorr1 = gsumi[j] * (1 - Math.pow(this.beta1, this.k)); // correct bias first moment estimate\n              var biasCorr2 = xsumi[j] * (1 - Math.pow(this.beta2, this.k)); // correct bias second moment estimate\n              var dx =  - this.learning_rate * biasCorr1 / (Math.sqrt(biasCorr2) + this.eps);\n              p[j] += dx;\n            } else if(this.method === 'adagrad') {\n              // adagrad update\n              gsumi[j] = gsumi[j] + gij * gij;\n              var dx = - this.learning_rate / Math.sqrt(gsumi[j] + this.eps) * gij;\n              p[j] += dx;\n            } else if(this.method === 'windowgrad') {\n              // this is adagrad but with a moving window weighted average\n              // so the gradient is not accumulated over the entire history of the run. \n              // it's also referred to as Idea #1 in Zeiler paper on Adadelta. Seems reasonable to me!\n              gsumi[j] = this.ro * gsumi[j] + (1-this.ro) * gij * gij;\n              var dx = - this.learning_rate / Math.sqrt(gsumi[j] + this.eps) * gij; // eps added for better conditioning\n              p[j] += dx;\n            } else if(this.method === 'adadelta') {\n              gsumi[j] = this.ro * gsumi[j] + (1-this.ro) * gij * gij;\n              var dx = - Math.sqrt((xsumi[j] + this.eps)/(gsumi[j] + this.eps)) * gij;\n              xsumi[j] = this.ro * xsumi[j] + (1-this.ro) * dx * dx; // yes, xsum lags behind gsum by 1.\n              p[j] += dx;\n            } else if(this.method === 'nesterov') {\n            \tvar dx = gsumi[j];\n            \tgsumi[j] = gsumi[j] * this.momentum + this.learning_rate * gij;\n                dx = this.momentum * dx - (1.0 + this.momentum) * gsumi[j];\n                p[j] += dx;\n            } else {\n              // assume SGD\n              if(this.momentum > 0.0) {\n                // momentum update\n                var dx = this.momentum * gsumi[j] - this.learning_rate * gij; // step\n                gsumi[j] = dx; // back this up for next iteration of momentum\n                p[j] += dx; // apply corrected gradient\n              } else {\n                // vanilla sgd\n                p[j] +=  - this.learning_rate * gij;\n              }\n            }\n            g[j] = 0.0; // zero out gradient so that we can begin accumulating anew\n          }\n        }\n      }\n\n      // appending softmax_loss for backwards compatibility, but from now on we will always use cost_loss\n      // in future, TODO: have to completely redo the way loss is done around the network as currently \n      // loss is a bit of a hack. Ideally, user should specify arbitrary number of loss functions on any layer\n      // and it should all be computed correctly and automatically. \n      return {fwd_time: fwd_time, bwd_time: bwd_time, \n              l2_decay_loss: l2_decay_loss, l1_decay_loss: l1_decay_loss,\n              cost_loss: cost_loss, softmax_loss: cost_loss, \n              loss: cost_loss + l1_decay_loss + l2_decay_loss}\n    }\n  }\n  \n  global.Trainer = Trainer;\n  global.SGDTrainer = Trainer; // backwards compatibility\n})(convnetjs);\n\n(function(global) {\n  \"use strict\";\n\n  // used utilities, make explicit local references\n  var randf = global.randf;\n  var randi = global.randi;\n  var Net = global.Net;\n  var Trainer = global.Trainer;\n  var maxmin = global.maxmin;\n  var randperm = global.randperm;\n  var weightedSample = global.weightedSample;\n  var getopt = global.getopt;\n  var arrUnique = global.arrUnique;\n\n  /*\n  A MagicNet takes data: a list of convnetjs.Vol(), and labels\n  which for now are assumed to be class indeces 0..K. MagicNet then:\n  - creates data folds for cross-validation\n  - samples candidate networks\n  - evaluates candidate networks on all data folds\n  - produces predictions by model-averaging the best networks\n  */\n  var MagicNet = function(data, labels, opt) {\n    var opt = opt || {};\n    if(typeof data === 'undefined') { data = []; }\n    if(typeof labels === 'undefined') { labels = []; }\n\n    // required inputs\n    this.data = data; // store these pointers to data\n    this.labels = labels;\n\n    // optional inputs\n    this.train_ratio = getopt(opt, 'train_ratio', 0.7);\n    this.num_folds = getopt(opt, 'num_folds', 10);\n    this.num_candidates = getopt(opt, 'num_candidates', 50); // we evaluate several in parallel\n    // how many epochs of data to train every network? for every fold?\n    // higher values mean higher accuracy in final results, but more expensive\n    this.num_epochs = getopt(opt, 'num_epochs', 50); \n    // number of best models to average during prediction. Usually higher = better\n    this.ensemble_size = getopt(opt, 'ensemble_size', 10);\n\n    // candidate parameters\n    this.batch_size_min = getopt(opt, 'batch_size_min', 10);\n    this.batch_size_max = getopt(opt, 'batch_size_max', 300);\n    this.l2_decay_min = getopt(opt, 'l2_decay_min', -4);\n    this.l2_decay_max = getopt(opt, 'l2_decay_max', 2);\n    this.learning_rate_min = getopt(opt, 'learning_rate_min', -4);\n    this.learning_rate_max = getopt(opt, 'learning_rate_max', 0);\n    this.momentum_min = getopt(opt, 'momentum_min', 0.9);\n    this.momentum_max = getopt(opt, 'momentum_max', 0.9);\n    this.neurons_min = getopt(opt, 'neurons_min', 5);\n    this.neurons_max = getopt(opt, 'neurons_max', 30);\n\n    // computed\n    this.folds = []; // data fold indices, gets filled by sampleFolds()\n    this.candidates = []; // candidate networks that are being currently evaluated\n    this.evaluated_candidates = []; // history of all candidates that were fully evaluated on all folds\n    this.unique_labels = arrUnique(labels);\n    this.iter = 0; // iteration counter, goes from 0 -> num_epochs * num_training_data\n    this.foldix = 0; // index of active fold\n\n    // callbacks\n    this.finish_fold_callback = null;\n    this.finish_batch_callback = null;\n\n    // initializations\n    if(this.data.length > 0) {\n      this.sampleFolds();\n      this.sampleCandidates();\n    }\n  };\n\n  MagicNet.prototype = {\n\n    // sets this.folds to a sampling of this.num_folds folds\n    sampleFolds: function() {\n      var N = this.data.length;\n      var num_train = Math.floor(this.train_ratio * N);\n      this.folds = []; // flush folds, if any\n      for(var i=0;i<this.num_folds;i++) {\n        var p = randperm(N);\n        this.folds.push({train_ix: p.slice(0, num_train), test_ix: p.slice(num_train, N)});\n      }\n    },\n\n    // returns a random candidate network\n    sampleCandidate: function() {\n      var input_depth = this.data[0].w.length;\n      var num_classes = this.unique_labels.length;\n\n      // sample network topology and hyperparameters\n      var layer_defs = [];\n      layer_defs.push({type:'input', out_sx:1, out_sy:1, out_depth: input_depth});\n      var nl = weightedSample([0,1,2,3], [0.2, 0.3, 0.3, 0.2]); // prefer nets with 1,2 hidden layers\n      for(var q=0;q<nl;q++) {\n        var ni = randi(this.neurons_min, this.neurons_max);\n        var act = ['tanh','maxout','relu'][randi(0,3)];\n        if(randf(0,1)<0.5) {\n          var dp = Math.random();\n          layer_defs.push({type:'fc', num_neurons: ni, activation: act, drop_prob: dp});\n        } else {\n          layer_defs.push({type:'fc', num_neurons: ni, activation: act});\n        }\n      }\n      layer_defs.push({type:'softmax', num_classes: num_classes});\n      var net = new Net();\n      net.makeLayers(layer_defs);\n\n      // sample training hyperparameters\n      var bs = randi(this.batch_size_min, this.batch_size_max); // batch size\n      var l2 = Math.pow(10, randf(this.l2_decay_min, this.l2_decay_max)); // l2 weight decay\n      var lr = Math.pow(10, randf(this.learning_rate_min, this.learning_rate_max)); // learning rate\n      var mom = randf(this.momentum_min, this.momentum_max); // momentum. Lets just use 0.9, works okay usually ;p\n      var tp = randf(0,1); // trainer type\n      var trainer_def;\n      if(tp<0.33) {\n        trainer_def = {method:'adadelta', batch_size:bs, l2_decay:l2};\n      } else if(tp<0.66) {\n        trainer_def = {method:'adagrad', learning_rate: lr, batch_size:bs, l2_decay:l2};\n      } else {\n        trainer_def = {method:'sgd', learning_rate: lr, momentum: mom, batch_size:bs, l2_decay:l2};\n      }\n      \n      var trainer = new Trainer(net, trainer_def);\n\n      var cand = {};\n      cand.acc = [];\n      cand.accv = 0; // this will maintained as sum(acc) for convenience\n      cand.layer_defs = layer_defs;\n      cand.trainer_def = trainer_def;\n      cand.net = net;\n      cand.trainer = trainer;\n      return cand;\n    },\n\n    // sets this.candidates with this.num_candidates candidate nets\n    sampleCandidates: function() {\n      this.candidates = []; // flush, if any\n      for(var i=0;i<this.num_candidates;i++) {\n        var cand = this.sampleCandidate();\n        this.candidates.push(cand);\n      }\n    },\n\n    step: function() {\n      \n      // run an example through current candidate\n      this.iter++;\n\n      // step all candidates on a random data point\n      var fold = this.folds[this.foldix]; // active fold\n      var dataix = fold.train_ix[randi(0, fold.train_ix.length)];\n      for(var k=0;k<this.candidates.length;k++) {\n        var x = this.data[dataix];\n        var l = this.labels[dataix];\n        this.candidates[k].trainer.train(x, l);\n      }\n\n      // process consequences: sample new folds, or candidates\n      var lastiter = this.num_epochs * fold.train_ix.length;\n      if(this.iter >= lastiter) {\n        // finished evaluation of this fold. Get final validation\n        // accuracies, record them, and go on to next fold.\n        var val_acc = this.evalValErrors();\n        for(var k=0;k<this.candidates.length;k++) {\n          var c = this.candidates[k];\n          c.acc.push(val_acc[k]);\n          c.accv += val_acc[k];\n        }\n        this.iter = 0; // reset step number\n        this.foldix++; // increment fold\n\n        if(this.finish_fold_callback !== null) {\n          this.finish_fold_callback();\n        }\n\n        if(this.foldix >= this.folds.length) {\n          // we finished all folds as well! Record these candidates\n          // and sample new ones to evaluate.\n          for(var k=0;k<this.candidates.length;k++) {\n            this.evaluated_candidates.push(this.candidates[k]);\n          }\n          // sort evaluated candidates according to accuracy achieved\n          this.evaluated_candidates.sort(function(a, b) { \n            return (a.accv / a.acc.length) \n                 > (b.accv / b.acc.length) \n                 ? -1 : 1;\n          });\n          // and clip only to the top few ones (lets place limit at 3*ensemble_size)\n          // otherwise there are concerns with keeping these all in memory \n          // if MagicNet is being evaluated for a very long time\n          if(this.evaluated_candidates.length > 3 * this.ensemble_size) {\n            this.evaluated_candidates = this.evaluated_candidates.slice(0, 3 * this.ensemble_size);\n          }\n          if(this.finish_batch_callback !== null) {\n            this.finish_batch_callback();\n          }\n          this.sampleCandidates(); // begin with new candidates\n          this.foldix = 0; // reset this\n        } else {\n          // we will go on to another fold. reset all candidates nets\n          for(var k=0;k<this.candidates.length;k++) {\n            var c = this.candidates[k];\n            var net = new Net();\n            net.makeLayers(c.layer_defs);\n            var trainer = new Trainer(net, c.trainer_def);\n            c.net = net;\n            c.trainer = trainer;\n          }\n        }\n      }\n    },\n\n    evalValErrors: function() {\n      // evaluate candidates on validation data and return performance of current networks\n      // as simple list\n      var vals = [];\n      var fold = this.folds[this.foldix]; // active fold\n      for(var k=0;k<this.candidates.length;k++) {\n        var net = this.candidates[k].net;\n        var v = 0.0;\n        for(var q=0;q<fold.test_ix.length;q++) {\n          var x = this.data[fold.test_ix[q]];\n          var l = this.labels[fold.test_ix[q]];\n          net.forward(x);\n          var yhat = net.getPrediction();\n          v += (yhat === l ? 1.0 : 0.0); // 0 1 loss\n        }\n        v /= fold.test_ix.length; // normalize\n        vals.push(v);\n      }\n      return vals;\n    },\n\n    // returns prediction scores for given test data point, as Vol\n    // uses an averaged prediction from the best ensemble_size models\n    // x is a Vol.\n    predict_soft: function(data) {\n      // forward prop the best networks\n      // and accumulate probabilities at last layer into a an output Vol\n\n      var eval_candidates = [];\n      var nv = 0;\n      if(this.evaluated_candidates.length === 0) {\n        // not sure what to do here, first batch of nets hasnt evaluated yet\n        // lets just predict with current candidates.\n        nv = this.candidates.length;\n        eval_candidates = this.candidates;\n      } else {\n        // forward prop the best networks from evaluated_candidates\n        nv = Math.min(this.ensemble_size, this.evaluated_candidates.length);\n        eval_candidates = this.evaluated_candidates\n      }\n\n      // forward nets of all candidates and average the predictions\n      var xout, n;\n      for(var j=0;j<nv;j++) {\n        var net = eval_candidates[j].net;\n        var x = net.forward(data);\n        if(j===0) { \n          xout = x; \n          n = x.w.length; \n        } else {\n          // add it on\n          for(var d=0;d<n;d++) {\n            xout.w[d] += x.w[d];\n          }\n        }\n      }\n      // produce average\n      for(var d=0;d<n;d++) {\n        xout.w[d] /= nv;\n      }\n      return xout;\n    },\n\n    predict: function(data) {\n      var xout = this.predict_soft(data);\n      if(xout.w.length !== 0) {\n        var stats = maxmin(xout.w);\n        var predicted_label = stats.maxi; \n      } else {\n        var predicted_label = -1; // error out\n      }\n      return predicted_label;\n\n    },\n\n    toJSON: function() {\n      // dump the top ensemble_size networks as a list\n      var nv = Math.min(this.ensemble_size, this.evaluated_candidates.length);\n      var json = {};\n      json.nets = [];\n      for(var i=0;i<nv;i++) {\n        json.nets.push(this.evaluated_candidates[i].net.toJSON());\n      }\n      return json;\n    },\n\n    fromJSON: function(json) {\n      this.ensemble_size = json.nets.length;\n      this.evaluated_candidates = [];\n      for(var i=0;i<this.ensemble_size;i++) {\n        var net = new Net();\n        net.fromJSON(json.nets[i]);\n        var dummy_candidate = {};\n        dummy_candidate.net = net;\n        this.evaluated_candidates.push(dummy_candidate);\n      }\n    },\n\n    // callback functions\n    // called when a fold is finished, while evaluating a batch\n    onFinishFold: function(f) { this.finish_fold_callback = f; },\n    // called when a batch of candidates has finished evaluating\n    onFinishBatch: function(f) { this.finish_batch_callback = f; }\n    \n  };\n\n  global.MagicNet = MagicNet;\n})(convnetjs);\n(function(lib) {\n  \"use strict\";\n  if (typeof module === \"undefined\" || typeof module.exports === \"undefined\") {\n    window.convnetjs = lib; // in ordinary browser attach library to window\n  } else {\n    module.exports = lib; // in nodejs\n  }\n})(convnetjs);\n"
  }
]