Repository: carpedm20/DCGAN-tensorflow
Branch: master
Commit: 62c9a2a7f745
Files: 16
Total size: 32.4 MB
Directory structure:
gitextract_xriuzdbv/
├── .gitignore
├── LICENSE
├── README.md
├── download.py
├── main.py
├── model.py
├── ops.py
├── utils.py
└── web/
├── app.py
├── css/
│ ├── fakeLoader.css
│ └── main.css
├── fonts/
│ └── FontAwesome.otf
├── index.html
└── js/
├── app.js
├── convnet.js
└── layers.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Data
data
samples
*.zip
logs
test*
web/js/gen_layers.js
# checkpoint
checkpoint
# trash
.dropbox
.DS_Store
# Created by https://www.gitignore.io/api/python,vim
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
### Vim ###
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
*.un~
Session.vim
.netrwhist
*~
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2016 Taehoon Kim
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# DCGAN in Tensorflow
Tensorflow 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).

* [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.
* *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.*
## Online Demo
[<img src="https://raw.githubusercontent.com/carpedm20/blog/master/content/images/face.png">](http://carpedm20.github.io/faces/)
[link](http://carpedm20.github.io/faces/)
## Prerequisites
- Python 2.7 or Python 3.3+
- [Tensorflow 0.12.1](https://github.com/tensorflow/tensorflow/tree/r0.12)
- [SciPy](http://www.scipy.org/install.html)
- [pillow](https://github.com/python-pillow/Pillow)
- [tqdm](https://pypi.org/project/tqdm/)
- (Optional) [moviepy](https://github.com/Zulko/moviepy) (for visualization)
- (Optional) [Align&Cropped Images.zip](http://mmlab.ie.cuhk.edu.hk/projects/CelebA.html) : Large-scale CelebFaces Dataset
## Usage
First, download dataset with:
$ python download.py mnist celebA
To train a model with downloaded dataset:
$ python main.py --dataset mnist --input_height=28 --output_height=28 --train
$ python main.py --dataset celebA --input_height=108 --train --crop
To test with an existing model:
$ python main.py --dataset mnist --input_height=28 --output_height=28
$ python main.py --dataset celebA --input_height=108 --crop
Or, you can use your own dataset (without central crop) by:
$ mkdir data/DATASET_NAME
... add images to data/DATASET_NAME ...
$ python main.py --dataset DATASET_NAME --train
$ python main.py --dataset DATASET_NAME
$ # example
$ python main.py --dataset=eyes --input_fname_pattern="*_cropped.png" --train
If your dataset is located in a different root directory:
$ python main.py --dataset DATASET_NAME --data_dir DATASET_ROOT_DIR --train
$ python main.py --dataset DATASET_NAME --data_dir DATASET_ROOT_DIR
$ # example
$ python main.py --dataset=eyes --data_dir ../datasets/ --input_fname_pattern="*_cropped.png" --train
## Results

### celebA
After 6th epoch:

After 10th epoch:

### Asian face dataset



### MNIST
MNIST codes are written by [@PhoenixDai](https://github.com/PhoenixDai).



More results can be found [here](./assets/) and [here](./web/img/).
## Training details
Details of the loss of Discriminator and Generator (with custom dataset not celebA).


Details of the histogram of true and fake result of discriminator (with custom dataset not celebA).


## Related works
- [BEGAN-tensorflow](https://github.com/carpedm20/BEGAN-tensorflow)
- [DiscoGAN-pytorch](https://github.com/carpedm20/DiscoGAN-pytorch)
- [simulated-unsupervised-tensorflow](https://github.com/carpedm20/simulated-unsupervised-tensorflow)
## Author
Taehoon Kim / [@carpedm20](http://carpedm20.github.io/)
================================================
FILE: download.py
================================================
"""
Modification of https://github.com/stanfordnlp/treelstm/blob/master/scripts/download.py
Downloads the following:
- Celeb-A dataset
- LSUN dataset
- MNIST dataset
"""
from __future__ import print_function
import os
import sys
import gzip
import json
import shutil
import zipfile
import argparse
import requests
import subprocess
from tqdm import tqdm
from six.moves import urllib
parser = argparse.ArgumentParser(description='Download dataset for DCGAN.')
parser.add_argument('datasets', metavar='N', type=str, nargs='+', choices=['celebA', 'lsun', 'mnist'],
help='name of dataset to download [celebA, lsun, mnist]')
def download(url, dirpath):
filename = url.split('/')[-1]
filepath = os.path.join(dirpath, filename)
u = urllib.request.urlopen(url)
f = open(filepath, 'wb')
filesize = int(u.headers["Content-Length"])
print("Downloading: %s Bytes: %s" % (filename, filesize))
downloaded = 0
block_sz = 8192
status_width = 70
while True:
buf = u.read(block_sz)
if not buf:
print('')
break
else:
print('', end='\r')
downloaded += len(buf)
f.write(buf)
status = (("[%-" + str(status_width + 1) + "s] %3.2f%%") %
('=' * int(float(downloaded) / filesize * status_width) + '>', downloaded * 100. / filesize))
print(status, end='')
sys.stdout.flush()
f.close()
return filepath
def download_file_from_google_drive(id, destination):
URL = "https://docs.google.com/uc?export=download"
session = requests.Session()
response = session.get(URL, params={ 'id': id }, stream=True)
token = get_confirm_token(response)
if token:
params = { 'id' : id, 'confirm' : token }
response = session.get(URL, params=params, stream=True)
save_response_content(response, destination)
def get_confirm_token(response):
for key, value in response.cookies.items():
if key.startswith('download_warning'):
return value
return None
def save_response_content(response, destination, chunk_size=32*1024):
total_size = int(response.headers.get('content-length', 0))
with open(destination, "wb") as f:
for chunk in tqdm(response.iter_content(chunk_size), total=total_size,
unit='B', unit_scale=True, desc=destination):
if chunk: # filter out keep-alive new chunks
f.write(chunk)
def unzip(filepath):
print("Extracting: " + filepath)
dirpath = os.path.dirname(filepath)
with zipfile.ZipFile(filepath) as zf:
zf.extractall(dirpath)
os.remove(filepath)
def download_celeb_a(dirpath):
data_dir = 'celebA'
if os.path.exists(os.path.join(dirpath, data_dir)):
print('Found Celeb-A - skip')
return
filename, drive_id = "img_align_celeba.zip", "0B7EVK8r0v71pZjFTYXZWM3FlRnM"
save_path = os.path.join(dirpath, filename)
if os.path.exists(save_path):
print('[*] {} already exists'.format(save_path))
else:
download_file_from_google_drive(drive_id, save_path)
zip_dir = ''
with zipfile.ZipFile(save_path) as zf:
zip_dir = zf.namelist()[0]
zf.extractall(dirpath)
os.remove(save_path)
os.rename(os.path.join(dirpath, zip_dir), os.path.join(dirpath, data_dir))
def _list_categories(tag):
url = 'http://lsun.cs.princeton.edu/htbin/list.cgi?tag=' + tag
f = urllib.request.urlopen(url)
return json.loads(f.read())
def _download_lsun(out_dir, category, set_name, tag):
url = 'http://lsun.cs.princeton.edu/htbin/download.cgi?tag={tag}' \
'&category={category}&set={set_name}'.format(**locals())
print(url)
if set_name == 'test':
out_name = 'test_lmdb.zip'
else:
out_name = '{category}_{set_name}_lmdb.zip'.format(**locals())
out_path = os.path.join(out_dir, out_name)
cmd = ['curl', url, '-o', out_path]
print('Downloading', category, set_name, 'set')
subprocess.call(cmd)
def download_lsun(dirpath):
data_dir = os.path.join(dirpath, 'lsun')
if os.path.exists(data_dir):
print('Found LSUN - skip')
return
else:
os.mkdir(data_dir)
tag = 'latest'
#categories = _list_categories(tag)
categories = ['bedroom']
for category in categories:
_download_lsun(data_dir, category, 'train', tag)
_download_lsun(data_dir, category, 'val', tag)
_download_lsun(data_dir, '', 'test', tag)
def download_mnist(dirpath):
data_dir = os.path.join(dirpath, 'mnist')
if os.path.exists(data_dir):
print('Found MNIST - skip')
return
else:
os.mkdir(data_dir)
url_base = 'http://yann.lecun.com/exdb/mnist/'
file_names = ['train-images-idx3-ubyte.gz',
'train-labels-idx1-ubyte.gz',
't10k-images-idx3-ubyte.gz',
't10k-labels-idx1-ubyte.gz']
for file_name in file_names:
url = (url_base+file_name).format(**locals())
print(url)
out_path = os.path.join(data_dir,file_name)
cmd = ['curl', url, '-o', out_path]
print('Downloading ', file_name)
subprocess.call(cmd)
cmd = ['gzip', '-d', out_path]
print('Decompressing ', file_name)
subprocess.call(cmd)
def prepare_data_dir(path = './data'):
if not os.path.exists(path):
os.mkdir(path)
if __name__ == '__main__':
args = parser.parse_args()
prepare_data_dir()
if any(name in args.datasets for name in ['CelebA', 'celebA', 'celebA']):
download_celeb_a('./data')
if 'lsun' in args.datasets:
download_lsun('./data')
if 'mnist' in args.datasets:
download_mnist('./data')
================================================
FILE: main.py
================================================
import os
import scipy.misc
import numpy as np
import json
from model import DCGAN
from utils import pp, visualize, to_json, show_all_variables, expand_path, timestamp
import tensorflow as tf
flags = tf.app.flags
flags.DEFINE_integer("epoch", 25, "Epoch to train [25]")
flags.DEFINE_float("learning_rate", 0.0002, "Learning rate of for adam [0.0002]")
flags.DEFINE_float("beta1", 0.5, "Momentum term of adam [0.5]")
flags.DEFINE_float("train_size", np.inf, "The size of train images [np.inf]")
flags.DEFINE_integer("batch_size", 64, "The size of batch images [64]")
flags.DEFINE_integer("input_height", 108, "The size of image to use (will be center cropped). [108]")
flags.DEFINE_integer("input_width", None, "The size of image to use (will be center cropped). If None, same value as input_height [None]")
flags.DEFINE_integer("output_height", 64, "The size of the output images to produce [64]")
flags.DEFINE_integer("output_width", None, "The size of the output images to produce. If None, same value as output_height [None]")
flags.DEFINE_string("dataset", "celebA", "The name of dataset [celebA, mnist, lsun]")
flags.DEFINE_string("input_fname_pattern", "*.jpg", "Glob pattern of filename of input images [*]")
flags.DEFINE_string("data_dir", "./data", "path to datasets [e.g. $HOME/data]")
flags.DEFINE_string("out_dir", "./out", "Root directory for outputs [e.g. $HOME/out]")
flags.DEFINE_string("out_name", "", "Folder (under out_root_dir) for all outputs. Generated automatically if left blank []")
flags.DEFINE_string("checkpoint_dir", "checkpoint", "Folder (under out_root_dir/out_name) to save checkpoints [checkpoint]")
flags.DEFINE_string("sample_dir", "samples", "Folder (under out_root_dir/out_name) to save samples [samples]")
flags.DEFINE_boolean("train", False, "True for training, False for testing [False]")
flags.DEFINE_boolean("crop", False, "True for training, False for testing [False]")
flags.DEFINE_boolean("visualize", False, "True for visualizing, False for nothing [False]")
flags.DEFINE_boolean("export", False, "True for exporting with new batch size")
flags.DEFINE_boolean("freeze", False, "True for exporting with new batch size")
flags.DEFINE_integer("max_to_keep", 1, "maximum number of checkpoints to keep")
flags.DEFINE_integer("sample_freq", 200, "sample every this many iterations")
flags.DEFINE_integer("ckpt_freq", 200, "save checkpoint every this many iterations")
flags.DEFINE_integer("z_dim", 100, "dimensions of z")
flags.DEFINE_string("z_dist", "uniform_signed", "'normal01' or 'uniform_unsigned' or uniform_signed")
flags.DEFINE_boolean("G_img_sum", False, "Save generator image summaries in log")
#flags.DEFINE_integer("generate_test_images", 100, "Number of images to generate during test. [100]")
FLAGS = flags.FLAGS
def main(_):
pp.pprint(flags.FLAGS.__flags)
# expand user name and environment variables
FLAGS.data_dir = expand_path(FLAGS.data_dir)
FLAGS.out_dir = expand_path(FLAGS.out_dir)
FLAGS.out_name = expand_path(FLAGS.out_name)
FLAGS.checkpoint_dir = expand_path(FLAGS.checkpoint_dir)
FLAGS.sample_dir = expand_path(FLAGS.sample_dir)
if FLAGS.output_height is None: FLAGS.output_height = FLAGS.input_height
if FLAGS.input_width is None: FLAGS.input_width = FLAGS.input_height
if FLAGS.output_width is None: FLAGS.output_width = FLAGS.output_height
# output folders
if FLAGS.out_name == "":
FLAGS.out_name = '{} - {} - {}'.format(timestamp(), FLAGS.data_dir.split('/')[-1], FLAGS.dataset) # penultimate folder of path
if FLAGS.train:
FLAGS.out_name += ' - x{}.z{}.{}.y{}.b{}'.format(FLAGS.input_width, FLAGS.z_dim, FLAGS.z_dist, FLAGS.output_width, FLAGS.batch_size)
FLAGS.out_dir = os.path.join(FLAGS.out_dir, FLAGS.out_name)
FLAGS.checkpoint_dir = os.path.join(FLAGS.out_dir, FLAGS.checkpoint_dir)
FLAGS.sample_dir = os.path.join(FLAGS.out_dir, FLAGS.sample_dir)
if not os.path.exists(FLAGS.checkpoint_dir): os.makedirs(FLAGS.checkpoint_dir)
if not os.path.exists(FLAGS.sample_dir): os.makedirs(FLAGS.sample_dir)
with open(os.path.join(FLAGS.out_dir, 'FLAGS.json'), 'w') as f:
flags_dict = {k:FLAGS[k].value for k in FLAGS}
json.dump(flags_dict, f, indent=4, sort_keys=True, ensure_ascii=False)
#gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=0.333)
run_config = tf.ConfigProto()
run_config.gpu_options.allow_growth=True
with tf.Session(config=run_config) as sess:
if FLAGS.dataset == 'mnist':
dcgan = DCGAN(
sess,
input_width=FLAGS.input_width,
input_height=FLAGS.input_height,
output_width=FLAGS.output_width,
output_height=FLAGS.output_height,
batch_size=FLAGS.batch_size,
sample_num=FLAGS.batch_size,
y_dim=10,
z_dim=FLAGS.z_dim,
dataset_name=FLAGS.dataset,
input_fname_pattern=FLAGS.input_fname_pattern,
crop=FLAGS.crop,
checkpoint_dir=FLAGS.checkpoint_dir,
sample_dir=FLAGS.sample_dir,
data_dir=FLAGS.data_dir,
out_dir=FLAGS.out_dir,
max_to_keep=FLAGS.max_to_keep)
else:
dcgan = DCGAN(
sess,
input_width=FLAGS.input_width,
input_height=FLAGS.input_height,
output_width=FLAGS.output_width,
output_height=FLAGS.output_height,
batch_size=FLAGS.batch_size,
sample_num=FLAGS.batch_size,
z_dim=FLAGS.z_dim,
dataset_name=FLAGS.dataset,
input_fname_pattern=FLAGS.input_fname_pattern,
crop=FLAGS.crop,
checkpoint_dir=FLAGS.checkpoint_dir,
sample_dir=FLAGS.sample_dir,
data_dir=FLAGS.data_dir,
out_dir=FLAGS.out_dir,
max_to_keep=FLAGS.max_to_keep)
show_all_variables()
if FLAGS.train:
dcgan.train(FLAGS)
else:
load_success, load_counter = dcgan.load(FLAGS.checkpoint_dir)
if not load_success:
raise Exception("Checkpoint not found in " + FLAGS.checkpoint_dir)
# to_json("./web/js/layers.js", [dcgan.h0_w, dcgan.h0_b, dcgan.g_bn0],
# [dcgan.h1_w, dcgan.h1_b, dcgan.g_bn1],
# [dcgan.h2_w, dcgan.h2_b, dcgan.g_bn2],
# [dcgan.h3_w, dcgan.h3_b, dcgan.g_bn3],
# [dcgan.h4_w, dcgan.h4_b, None])
# Below is codes for visualization
if FLAGS.export:
export_dir = os.path.join(FLAGS.checkpoint_dir, 'export_b'+str(FLAGS.batch_size))
dcgan.save(export_dir, load_counter, ckpt=True, frozen=False)
if FLAGS.freeze:
export_dir = os.path.join(FLAGS.checkpoint_dir, 'frozen_b'+str(FLAGS.batch_size))
dcgan.save(export_dir, load_counter, ckpt=False, frozen=True)
if FLAGS.visualize:
OPTION = 1
visualize(sess, dcgan, FLAGS, OPTION, FLAGS.sample_dir)
if __name__ == '__main__':
tf.app.run()
================================================
FILE: model.py
================================================
from __future__ import division
from __future__ import print_function
import os
import time
import math
from glob import glob
import tensorflow as tf
import numpy as np
from six.moves import xrange
from ops import *
from utils import *
def conv_out_size_same(size, stride):
return int(math.ceil(float(size) / float(stride)))
def gen_random(mode, size):
if mode=='normal01': return np.random.normal(0,1,size=size)
if mode=='uniform_signed': return np.random.uniform(-1,1,size=size)
if mode=='uniform_unsigned': return np.random.uniform(0,1,size=size)
class DCGAN(object):
def __init__(self, sess, input_height=108, input_width=108, crop=True,
batch_size=64, sample_num = 64, output_height=64, output_width=64,
y_dim=None, z_dim=100, gf_dim=64, df_dim=64,
gfc_dim=1024, dfc_dim=1024, c_dim=3, dataset_name='default',
max_to_keep=1,
input_fname_pattern='*.jpg', checkpoint_dir='ckpts', sample_dir='samples', out_dir='./out', data_dir='./data'):
"""
Args:
sess: TensorFlow session
batch_size: The size of batch. Should be specified before training.
y_dim: (optional) Dimension of dim for y. [None]
z_dim: (optional) Dimension of dim for Z. [100]
gf_dim: (optional) Dimension of gen filters in first conv layer. [64]
df_dim: (optional) Dimension of discrim filters in first conv layer. [64]
gfc_dim: (optional) Dimension of gen units for for fully connected layer. [1024]
dfc_dim: (optional) Dimension of discrim units for fully connected layer. [1024]
c_dim: (optional) Dimension of image color. For grayscale input, set to 1. [3]
"""
self.sess = sess
self.crop = crop
self.batch_size = batch_size
self.sample_num = sample_num
self.input_height = input_height
self.input_width = input_width
self.output_height = output_height
self.output_width = output_width
self.y_dim = y_dim
self.z_dim = z_dim
self.gf_dim = gf_dim
self.df_dim = df_dim
self.gfc_dim = gfc_dim
self.dfc_dim = dfc_dim
# batch normalization : deals with poor initialization helps gradient flow
self.d_bn1 = batch_norm(name='d_bn1')
self.d_bn2 = batch_norm(name='d_bn2')
if not self.y_dim:
self.d_bn3 = batch_norm(name='d_bn3')
self.g_bn0 = batch_norm(name='g_bn0')
self.g_bn1 = batch_norm(name='g_bn1')
self.g_bn2 = batch_norm(name='g_bn2')
if not self.y_dim:
self.g_bn3 = batch_norm(name='g_bn3')
self.dataset_name = dataset_name
self.input_fname_pattern = input_fname_pattern
self.checkpoint_dir = checkpoint_dir
self.data_dir = data_dir
self.out_dir = out_dir
self.max_to_keep = max_to_keep
if self.dataset_name == 'mnist':
self.data_X, self.data_y = self.load_mnist()
self.c_dim = self.data_X[0].shape[-1]
else:
data_path = os.path.join(self.data_dir, self.dataset_name, self.input_fname_pattern)
self.data = glob(data_path)
if len(self.data) == 0:
raise Exception("[!] No data found in '" + data_path + "'")
np.random.shuffle(self.data)
imreadImg = imread(self.data[0])
if len(imreadImg.shape) >= 3: #check if image is a non-grayscale image by checking channel number
self.c_dim = imread(self.data[0]).shape[-1]
else:
self.c_dim = 1
if len(self.data) < self.batch_size:
raise Exception("[!] Entire dataset size is less than the configured batch_size")
self.grayscale = (self.c_dim == 1)
self.build_model()
def build_model(self):
if self.y_dim:
self.y = tf.placeholder(tf.float32, [self.batch_size, self.y_dim], name='y')
else:
self.y = None
if self.crop:
image_dims = [self.output_height, self.output_width, self.c_dim]
else:
image_dims = [self.input_height, self.input_width, self.c_dim]
self.inputs = tf.placeholder(
tf.float32, [self.batch_size] + image_dims, name='real_images')
inputs = self.inputs
self.z = tf.placeholder(
tf.float32, [None, self.z_dim], name='z')
self.z_sum = histogram_summary("z", self.z)
self.G = self.generator(self.z, self.y)
self.D, self.D_logits = self.discriminator(inputs, self.y, reuse=False)
self.sampler = self.sampler(self.z, self.y)
self.D_, self.D_logits_ = self.discriminator(self.G, self.y, reuse=True)
self.d_sum = histogram_summary("d", self.D)
self.d__sum = histogram_summary("d_", self.D_)
self.G_sum = image_summary("G", self.G)
def sigmoid_cross_entropy_with_logits(x, y):
try:
return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, labels=y)
except:
return tf.nn.sigmoid_cross_entropy_with_logits(logits=x, targets=y)
self.d_loss_real = tf.reduce_mean(
sigmoid_cross_entropy_with_logits(self.D_logits, tf.ones_like(self.D)))
self.d_loss_fake = tf.reduce_mean(
sigmoid_cross_entropy_with_logits(self.D_logits_, tf.zeros_like(self.D_)))
self.g_loss = tf.reduce_mean(
sigmoid_cross_entropy_with_logits(self.D_logits_, tf.ones_like(self.D_)))
self.d_loss_real_sum = scalar_summary("d_loss_real", self.d_loss_real)
self.d_loss_fake_sum = scalar_summary("d_loss_fake", self.d_loss_fake)
self.d_loss = self.d_loss_real + self.d_loss_fake
self.g_loss_sum = scalar_summary("g_loss", self.g_loss)
self.d_loss_sum = scalar_summary("d_loss", self.d_loss)
t_vars = tf.trainable_variables()
self.d_vars = [var for var in t_vars if 'd_' in var.name]
self.g_vars = [var for var in t_vars if 'g_' in var.name]
self.saver = tf.train.Saver(max_to_keep=self.max_to_keep)
def train(self, config):
d_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \
.minimize(self.d_loss, var_list=self.d_vars)
g_optim = tf.train.AdamOptimizer(config.learning_rate, beta1=config.beta1) \
.minimize(self.g_loss, var_list=self.g_vars)
try:
tf.global_variables_initializer().run()
except:
tf.initialize_all_variables().run()
if config.G_img_sum:
self.g_sum = merge_summary([self.z_sum, self.d__sum, self.G_sum, self.d_loss_fake_sum, self.g_loss_sum])
else:
self.g_sum = merge_summary([self.z_sum, self.d__sum, self.d_loss_fake_sum, self.g_loss_sum])
self.d_sum = merge_summary(
[self.z_sum, self.d_sum, self.d_loss_real_sum, self.d_loss_sum])
self.writer = SummaryWriter(os.path.join(self.out_dir, "logs"), self.sess.graph)
sample_z = gen_random(config.z_dist, size=(self.sample_num , self.z_dim))
if config.dataset == 'mnist':
sample_inputs = self.data_X[0:self.sample_num]
sample_labels = self.data_y[0:self.sample_num]
else:
sample_files = self.data[0:self.sample_num]
sample = [
get_image(sample_file,
input_height=self.input_height,
input_width=self.input_width,
resize_height=self.output_height,
resize_width=self.output_width,
crop=self.crop,
grayscale=self.grayscale) for sample_file in sample_files]
if (self.grayscale):
sample_inputs = np.array(sample).astype(np.float32)[:, :, :, None]
else:
sample_inputs = np.array(sample).astype(np.float32)
counter = 1
start_time = time.time()
could_load, checkpoint_counter = self.load(self.checkpoint_dir)
if could_load:
counter = checkpoint_counter
print(" [*] Load SUCCESS")
else:
print(" [!] Load failed...")
for epoch in xrange(config.epoch):
if config.dataset == 'mnist':
batch_idxs = min(len(self.data_X), config.train_size) // config.batch_size
else:
self.data = glob(os.path.join(
config.data_dir, config.dataset, self.input_fname_pattern))
np.random.shuffle(self.data)
batch_idxs = min(len(self.data), config.train_size) // config.batch_size
for idx in xrange(0, int(batch_idxs)):
if config.dataset == 'mnist':
batch_images = self.data_X[idx*config.batch_size:(idx+1)*config.batch_size]
batch_labels = self.data_y[idx*config.batch_size:(idx+1)*config.batch_size]
else:
batch_files = self.data[idx*config.batch_size:(idx+1)*config.batch_size]
batch = [
get_image(batch_file,
input_height=self.input_height,
input_width=self.input_width,
resize_height=self.output_height,
resize_width=self.output_width,
crop=self.crop,
grayscale=self.grayscale) for batch_file in batch_files]
if self.grayscale:
batch_images = np.array(batch).astype(np.float32)[:, :, :, None]
else:
batch_images = np.array(batch).astype(np.float32)
batch_z = gen_random(config.z_dist, size=[config.batch_size, self.z_dim]) \
.astype(np.float32)
if config.dataset == 'mnist':
# Update D network
_, summary_str = self.sess.run([d_optim, self.d_sum],
feed_dict={
self.inputs: batch_images,
self.z: batch_z,
self.y:batch_labels,
})
self.writer.add_summary(summary_str, counter)
# Update G network
_, summary_str = self.sess.run([g_optim, self.g_sum],
feed_dict={
self.z: batch_z,
self.y:batch_labels,
})
self.writer.add_summary(summary_str, counter)
# Run g_optim twice to make sure that d_loss does not go to zero (different from paper)
_, summary_str = self.sess.run([g_optim, self.g_sum],
feed_dict={ self.z: batch_z, self.y:batch_labels })
self.writer.add_summary(summary_str, counter)
errD_fake = self.d_loss_fake.eval({
self.z: batch_z,
self.y:batch_labels
})
errD_real = self.d_loss_real.eval({
self.inputs: batch_images,
self.y:batch_labels
})
errG = self.g_loss.eval({
self.z: batch_z,
self.y: batch_labels
})
else:
# Update D network
_, summary_str = self.sess.run([d_optim, self.d_sum],
feed_dict={ self.inputs: batch_images, self.z: batch_z })
self.writer.add_summary(summary_str, counter)
# Update G network
_, summary_str = self.sess.run([g_optim, self.g_sum],
feed_dict={ self.z: batch_z })
self.writer.add_summary(summary_str, counter)
# Run g_optim twice to make sure that d_loss does not go to zero (different from paper)
_, summary_str = self.sess.run([g_optim, self.g_sum],
feed_dict={ self.z: batch_z })
self.writer.add_summary(summary_str, counter)
errD_fake = self.d_loss_fake.eval({ self.z: batch_z })
errD_real = self.d_loss_real.eval({ self.inputs: batch_images })
errG = self.g_loss.eval({self.z: batch_z})
print("[%8d Epoch:[%2d/%2d] [%4d/%4d] time: %4.4f, d_loss: %.8f, g_loss: %.8f" \
% (counter, epoch, config.epoch, idx, batch_idxs,
time.time() - start_time, errD_fake+errD_real, errG))
if np.mod(counter, config.sample_freq) == 0:
if config.dataset == 'mnist':
samples, d_loss, g_loss = self.sess.run(
[self.sampler, self.d_loss, self.g_loss],
feed_dict={
self.z: sample_z,
self.inputs: sample_inputs,
self.y:sample_labels,
}
)
save_images(samples, image_manifold_size(samples.shape[0]),
'./{}/train_{:08d}.png'.format(config.sample_dir, counter))
print("[Sample] d_loss: %.8f, g_loss: %.8f" % (d_loss, g_loss))
else:
try:
samples, d_loss, g_loss = self.sess.run(
[self.sampler, self.d_loss, self.g_loss],
feed_dict={
self.z: sample_z,
self.inputs: sample_inputs,
},
)
save_images(samples, image_manifold_size(samples.shape[0]),
'./{}/train_{:08d}.png'.format(config.sample_dir, counter))
print("[Sample] d_loss: %.8f, g_loss: %.8f" % (d_loss, g_loss))
except:
print("one pic error!...")
if np.mod(counter, config.ckpt_freq) == 0:
self.save(config.checkpoint_dir, counter)
counter += 1
def discriminator(self, image, y=None, reuse=False):
with tf.variable_scope("discriminator") as scope:
if reuse:
scope.reuse_variables()
if not self.y_dim:
h0 = lrelu(conv2d(image, self.df_dim, name='d_h0_conv'))
h1 = lrelu(self.d_bn1(conv2d(h0, self.df_dim*2, name='d_h1_conv')))
h2 = lrelu(self.d_bn2(conv2d(h1, self.df_dim*4, name='d_h2_conv')))
h3 = lrelu(self.d_bn3(conv2d(h2, self.df_dim*8, name='d_h3_conv')))
h4 = linear(tf.reshape(h3, [self.batch_size, -1]), 1, 'd_h4_lin')
return tf.nn.sigmoid(h4), h4
else:
yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
x = conv_cond_concat(image, yb)
h0 = lrelu(conv2d(x, self.c_dim + self.y_dim, name='d_h0_conv'))
h0 = conv_cond_concat(h0, yb)
h1 = lrelu(self.d_bn1(conv2d(h0, self.df_dim + self.y_dim, name='d_h1_conv')))
h1 = tf.reshape(h1, [self.batch_size, -1])
h1 = concat([h1, y], 1)
h2 = lrelu(self.d_bn2(linear(h1, self.dfc_dim, 'd_h2_lin')))
h2 = concat([h2, y], 1)
h3 = linear(h2, 1, 'd_h3_lin')
return tf.nn.sigmoid(h3), h3
def generator(self, z, y=None):
with tf.variable_scope("generator") as scope:
if not self.y_dim:
s_h, s_w = self.output_height, self.output_width
s_h2, s_w2 = conv_out_size_same(s_h, 2), conv_out_size_same(s_w, 2)
s_h4, s_w4 = conv_out_size_same(s_h2, 2), conv_out_size_same(s_w2, 2)
s_h8, s_w8 = conv_out_size_same(s_h4, 2), conv_out_size_same(s_w4, 2)
s_h16, s_w16 = conv_out_size_same(s_h8, 2), conv_out_size_same(s_w8, 2)
# project `z` and reshape
self.z_, self.h0_w, self.h0_b = linear(
z, self.gf_dim*8*s_h16*s_w16, 'g_h0_lin', with_w=True)
self.h0 = tf.reshape(
self.z_, [-1, s_h16, s_w16, self.gf_dim * 8])
h0 = tf.nn.relu(self.g_bn0(self.h0))
self.h1, self.h1_w, self.h1_b = deconv2d(
h0, [self.batch_size, s_h8, s_w8, self.gf_dim*4], name='g_h1', with_w=True)
h1 = tf.nn.relu(self.g_bn1(self.h1))
h2, self.h2_w, self.h2_b = deconv2d(
h1, [self.batch_size, s_h4, s_w4, self.gf_dim*2], name='g_h2', with_w=True)
h2 = tf.nn.relu(self.g_bn2(h2))
h3, self.h3_w, self.h3_b = deconv2d(
h2, [self.batch_size, s_h2, s_w2, self.gf_dim*1], name='g_h3', with_w=True)
h3 = tf.nn.relu(self.g_bn3(h3))
h4, self.h4_w, self.h4_b = deconv2d(
h3, [self.batch_size, s_h, s_w, self.c_dim], name='g_h4', with_w=True)
return tf.nn.tanh(h4)
else:
s_h, s_w = self.output_height, self.output_width
s_h2, s_h4 = int(s_h/2), int(s_h/4)
s_w2, s_w4 = int(s_w/2), int(s_w/4)
# yb = tf.expand_dims(tf.expand_dims(y, 1),2)
yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
z = concat([z, y], 1)
h0 = tf.nn.relu(
self.g_bn0(linear(z, self.gfc_dim, 'g_h0_lin')))
h0 = concat([h0, y], 1)
h1 = tf.nn.relu(self.g_bn1(
linear(h0, self.gf_dim*2*s_h4*s_w4, 'g_h1_lin')))
h1 = tf.reshape(h1, [self.batch_size, s_h4, s_w4, self.gf_dim * 2])
h1 = conv_cond_concat(h1, yb)
h2 = tf.nn.relu(self.g_bn2(deconv2d(h1,
[self.batch_size, s_h2, s_w2, self.gf_dim * 2], name='g_h2')))
h2 = conv_cond_concat(h2, yb)
return tf.nn.sigmoid(
deconv2d(h2, [self.batch_size, s_h, s_w, self.c_dim], name='g_h3'))
def sampler(self, z, y=None):
with tf.variable_scope("generator") as scope:
scope.reuse_variables()
if not self.y_dim:
s_h, s_w = self.output_height, self.output_width
s_h2, s_w2 = conv_out_size_same(s_h, 2), conv_out_size_same(s_w, 2)
s_h4, s_w4 = conv_out_size_same(s_h2, 2), conv_out_size_same(s_w2, 2)
s_h8, s_w8 = conv_out_size_same(s_h4, 2), conv_out_size_same(s_w4, 2)
s_h16, s_w16 = conv_out_size_same(s_h8, 2), conv_out_size_same(s_w8, 2)
# project `z` and reshape
h0 = tf.reshape(
linear(z, self.gf_dim*8*s_h16*s_w16, 'g_h0_lin'),
[-1, s_h16, s_w16, self.gf_dim * 8])
h0 = tf.nn.relu(self.g_bn0(h0, train=False))
h1 = deconv2d(h0, [self.batch_size, s_h8, s_w8, self.gf_dim*4], name='g_h1')
h1 = tf.nn.relu(self.g_bn1(h1, train=False))
h2 = deconv2d(h1, [self.batch_size, s_h4, s_w4, self.gf_dim*2], name='g_h2')
h2 = tf.nn.relu(self.g_bn2(h2, train=False))
h3 = deconv2d(h2, [self.batch_size, s_h2, s_w2, self.gf_dim*1], name='g_h3')
h3 = tf.nn.relu(self.g_bn3(h3, train=False))
h4 = deconv2d(h3, [self.batch_size, s_h, s_w, self.c_dim], name='g_h4')
return tf.nn.tanh(h4)
else:
s_h, s_w = self.output_height, self.output_width
s_h2, s_h4 = int(s_h/2), int(s_h/4)
s_w2, s_w4 = int(s_w/2), int(s_w/4)
# yb = tf.reshape(y, [-1, 1, 1, self.y_dim])
yb = tf.reshape(y, [self.batch_size, 1, 1, self.y_dim])
z = concat([z, y], 1)
h0 = tf.nn.relu(self.g_bn0(linear(z, self.gfc_dim, 'g_h0_lin'), train=False))
h0 = concat([h0, y], 1)
h1 = tf.nn.relu(self.g_bn1(
linear(h0, self.gf_dim*2*s_h4*s_w4, 'g_h1_lin'), train=False))
h1 = tf.reshape(h1, [self.batch_size, s_h4, s_w4, self.gf_dim * 2])
h1 = conv_cond_concat(h1, yb)
h2 = tf.nn.relu(self.g_bn2(
deconv2d(h1, [self.batch_size, s_h2, s_w2, self.gf_dim * 2], name='g_h2'), train=False))
h2 = conv_cond_concat(h2, yb)
return tf.nn.sigmoid(deconv2d(h2, [self.batch_size, s_h, s_w, self.c_dim], name='g_h3'))
def load_mnist(self):
data_dir = os.path.join(self.data_dir, self.dataset_name)
fd = open(os.path.join(data_dir,'train-images-idx3-ubyte'))
loaded = np.fromfile(file=fd,dtype=np.uint8)
trX = loaded[16:].reshape((60000,28,28,1)).astype(np.float)
fd = open(os.path.join(data_dir,'train-labels-idx1-ubyte'))
loaded = np.fromfile(file=fd,dtype=np.uint8)
trY = loaded[8:].reshape((60000)).astype(np.float)
fd = open(os.path.join(data_dir,'t10k-images-idx3-ubyte'))
loaded = np.fromfile(file=fd,dtype=np.uint8)
teX = loaded[16:].reshape((10000,28,28,1)).astype(np.float)
fd = open(os.path.join(data_dir,'t10k-labels-idx1-ubyte'))
loaded = np.fromfile(file=fd,dtype=np.uint8)
teY = loaded[8:].reshape((10000)).astype(np.float)
trY = np.asarray(trY)
teY = np.asarray(teY)
X = np.concatenate((trX, teX), axis=0)
y = np.concatenate((trY, teY), axis=0).astype(np.int)
seed = 547
np.random.seed(seed)
np.random.shuffle(X)
np.random.seed(seed)
np.random.shuffle(y)
y_vec = np.zeros((len(y), self.y_dim), dtype=np.float)
for i, label in enumerate(y):
y_vec[i,y[i]] = 1.0
return X/255.,y_vec
@property
def model_dir(self):
return "{}_{}_{}_{}".format(
self.dataset_name, self.batch_size,
self.output_height, self.output_width)
def save(self, checkpoint_dir, step, filename='model', ckpt=True, frozen=False):
# model_name = "DCGAN.model"
# checkpoint_dir = os.path.join(checkpoint_dir, self.model_dir)
filename += '.b' + str(self.batch_size)
if not os.path.exists(checkpoint_dir):
os.makedirs(checkpoint_dir)
if ckpt:
self.saver.save(self.sess,
os.path.join(checkpoint_dir, filename),
global_step=step)
if frozen:
tf.train.write_graph(
tf.graph_util.convert_variables_to_constants(self.sess, self.sess.graph_def, ["generator_1/Tanh"]),
checkpoint_dir,
'{}-{:06d}_frz.pb'.format(filename, step),
as_text=False)
def load(self, checkpoint_dir):
#import re
print(" [*] Reading checkpoints...", checkpoint_dir)
# checkpoint_dir = os.path.join(checkpoint_dir, self.model_dir)
# print(" ->", checkpoint_dir)
ckpt = tf.train.get_checkpoint_state(checkpoint_dir)
if ckpt and ckpt.model_checkpoint_path:
ckpt_name = os.path.basename(ckpt.model_checkpoint_path)
self.saver.restore(self.sess, os.path.join(checkpoint_dir, ckpt_name))
#counter = int(next(re.finditer("(\d+)(?!.*\d)",ckpt_name)).group(0))
counter = int(ckpt_name.split('-')[-1])
print(" [*] Success to read {}".format(ckpt_name))
return True, counter
else:
print(" [*] Failed to find a checkpoint")
return False, 0
================================================
FILE: ops.py
================================================
import math
import numpy as np
import tensorflow as tf
from tensorflow.python.framework import ops
from utils import *
try:
image_summary = tf.image_summary
scalar_summary = tf.scalar_summary
histogram_summary = tf.histogram_summary
merge_summary = tf.merge_summary
SummaryWriter = tf.train.SummaryWriter
except:
image_summary = tf.summary.image
scalar_summary = tf.summary.scalar
histogram_summary = tf.summary.histogram
merge_summary = tf.summary.merge
SummaryWriter = tf.summary.FileWriter
if "concat_v2" in dir(tf):
def concat(tensors, axis, *args, **kwargs):
return tf.concat_v2(tensors, axis, *args, **kwargs)
else:
def concat(tensors, axis, *args, **kwargs):
return tf.concat(tensors, axis, *args, **kwargs)
class batch_norm(object):
def __init__(self, epsilon=1e-5, momentum = 0.9, name="batch_norm"):
with tf.variable_scope(name):
self.epsilon = epsilon
self.momentum = momentum
self.name = name
def __call__(self, x, train=True):
return tf.contrib.layers.batch_norm(x,
decay=self.momentum,
updates_collections=None,
epsilon=self.epsilon,
scale=True,
is_training=train,
scope=self.name)
def conv_cond_concat(x, y):
"""Concatenate conditioning vector on feature map axis."""
x_shapes = x.get_shape()
y_shapes = y.get_shape()
return concat([
x, y*tf.ones([x_shapes[0], x_shapes[1], x_shapes[2], y_shapes[3]])], 3)
def conv2d(input_, output_dim,
k_h=5, k_w=5, d_h=2, d_w=2, stddev=0.02,
name="conv2d"):
with tf.variable_scope(name):
w = tf.get_variable('w', [k_h, k_w, input_.get_shape()[-1], output_dim],
initializer=tf.truncated_normal_initializer(stddev=stddev))
conv = tf.nn.conv2d(input_, w, strides=[1, d_h, d_w, 1], padding='SAME')
biases = tf.get_variable('biases', [output_dim], initializer=tf.constant_initializer(0.0))
conv = tf.reshape(tf.nn.bias_add(conv, biases), conv.get_shape())
return conv
def deconv2d(input_, output_shape,
k_h=5, k_w=5, d_h=2, d_w=2, stddev=0.02,
name="deconv2d", with_w=False):
with tf.variable_scope(name):
# filter : [height, width, output_channels, in_channels]
w = tf.get_variable('w', [k_h, k_w, output_shape[-1], input_.get_shape()[-1]],
initializer=tf.random_normal_initializer(stddev=stddev))
try:
deconv = tf.nn.conv2d_transpose(input_, w, output_shape=output_shape,
strides=[1, d_h, d_w, 1])
# Support for verisons of TensorFlow before 0.7.0
except AttributeError:
deconv = tf.nn.deconv2d(input_, w, output_shape=output_shape,
strides=[1, d_h, d_w, 1])
biases = tf.get_variable('biases', [output_shape[-1]], initializer=tf.constant_initializer(0.0))
deconv = tf.reshape(tf.nn.bias_add(deconv, biases), deconv.get_shape())
if with_w:
return deconv, w, biases
else:
return deconv
def lrelu(x, leak=0.2, name="lrelu"):
return tf.maximum(x, leak*x)
def linear(input_, output_size, scope=None, stddev=0.02, bias_start=0.0, with_w=False):
shape = input_.get_shape().as_list()
with tf.variable_scope(scope or "Linear"):
try:
matrix = tf.get_variable("Matrix", [shape[1], output_size], tf.float32,
tf.random_normal_initializer(stddev=stddev))
except ValueError as err:
msg = "NOTE: Usually, this is due to an issue with the image dimensions. Did you correctly set '--crop' or '--input_height' or '--output_height'?"
err.args = err.args + (msg,)
raise
bias = tf.get_variable("bias", [output_size],
initializer=tf.constant_initializer(bias_start))
if with_w:
return tf.matmul(input_, matrix) + bias, matrix, bias
else:
return tf.matmul(input_, matrix) + bias
================================================
FILE: utils.py
================================================
"""
Some codes from https://github.com/Newmu/dcgan_code
"""
from __future__ import division
import math
import json
import random
import pprint
import scipy.misc
import cv2
import numpy as np
import os
import time
import datetime
from time import gmtime, strftime
from six.moves import xrange
from PIL import Image
import tensorflow as tf
import tensorflow.contrib.slim as slim
pp = pprint.PrettyPrinter()
get_stddev = lambda x, k_h, k_w: 1/math.sqrt(k_w*k_h*x.get_shape()[-1])
def expand_path(path):
return os.path.expanduser(os.path.expandvars(path))
def timestamp(s='%Y%m%d.%H%M%S', ts=None):
if not ts: ts = time.time()
st = datetime.datetime.fromtimestamp(ts).strftime(s)
return st
def show_all_variables():
model_vars = tf.trainable_variables()
slim.model_analyzer.analyze_vars(model_vars, print_info=True)
def get_image(image_path, input_height, input_width,
resize_height=64, resize_width=64,
crop=True, grayscale=False):
image = imread(image_path, grayscale)
return transform(image, input_height, input_width,
resize_height, resize_width, crop)
def save_images(images, size, image_path):
return imsave(inverse_transform(images), size, image_path)
def imread(path, grayscale = False):
if (grayscale):
return scipy.misc.imread(path, flatten = True).astype(np.float)
else:
# Reference: https://github.com/carpedm20/DCGAN-tensorflow/issues/162#issuecomment-315519747
img_bgr = cv2.imread(path)
# Reference: https://stackoverflow.com/a/15074748/
img_rgb = img_bgr[..., ::-1]
return img_rgb.astype(np.float)
def merge_images(images, size):
return inverse_transform(images)
def merge(images, size):
h, w = images.shape[1], images.shape[2]
if (images.shape[3] in (3,4)):
c = images.shape[3]
img = np.zeros((h * size[0], w * size[1], c))
for idx, image in enumerate(images):
i = idx % size[1]
j = idx // size[1]
img[j * h:j * h + h, i * w:i * w + w, :] = image
return img
elif images.shape[3]==1:
img = np.zeros((h * size[0], w * size[1]))
for idx, image in enumerate(images):
i = idx % size[1]
j = idx // size[1]
img[j * h:j * h + h, i * w:i * w + w] = image[:,:,0]
return img
else:
raise ValueError('in merge(images,size) images parameter '
'must have dimensions: HxW or HxWx3 or HxWx4')
def imsave(images, size, path):
image = np.squeeze(merge(images, size))
return scipy.misc.imsave(path, image)
def center_crop(x, crop_h, crop_w,
resize_h=64, resize_w=64):
if crop_w is None:
crop_w = crop_h
h, w = x.shape[:2]
j = int(round((h - crop_h)/2.))
i = int(round((w - crop_w)/2.))
im = Image.fromarray(x[j:j+crop_h, i:i+crop_w])
return np.array(im.resize([resize_h, resize_w]), PIL.Image.BILINEAR)
def transform(image, input_height, input_width,
resize_height=64, resize_width=64, crop=True):
if crop:
cropped_image = center_crop(
image, input_height, input_width,
resize_height, resize_width)
else:
im = Image.fromarray(image[j:j+crop_h, i:i+crop_w])
return np.array(im.resize([resize_h, resize_w]), PIL.Image.BILINEAR)/127.5 - 1.
def inverse_transform(images):
return (images+1.)/2.
def to_json(output_path, *layers):
with open(output_path, "w") as layer_f:
lines = ""
for w, b, bn in layers:
layer_idx = w.name.split('/')[0].split('h')[1]
B = b.eval()
if "lin/" in w.name:
W = w.eval()
depth = W.shape[1]
else:
W = np.rollaxis(w.eval(), 2, 0)
depth = W.shape[0]
biases = {"sy": 1, "sx": 1, "depth": depth, "w": ['%.2f' % elem for elem in list(B)]}
if bn != None:
gamma = bn.gamma.eval()
beta = bn.beta.eval()
gamma = {"sy": 1, "sx": 1, "depth": depth, "w": ['%.2f' % elem for elem in list(gamma)]}
beta = {"sy": 1, "sx": 1, "depth": depth, "w": ['%.2f' % elem for elem in list(beta)]}
else:
gamma = {"sy": 1, "sx": 1, "depth": 0, "w": []}
beta = {"sy": 1, "sx": 1, "depth": 0, "w": []}
if "lin/" in w.name:
fs = []
for w in W.T:
fs.append({"sy": 1, "sx": 1, "depth": W.shape[0], "w": ['%.2f' % elem for elem in list(w)]})
lines += """
var layer_%s = {
"layer_type": "fc",
"sy": 1, "sx": 1,
"out_sx": 1, "out_sy": 1,
"stride": 1, "pad": 0,
"out_depth": %s, "in_depth": %s,
"biases": %s,
"gamma": %s,
"beta": %s,
"filters": %s
};""" % (layer_idx.split('_')[0], W.shape[1], W.shape[0], biases, gamma, beta, fs)
else:
fs = []
for w_ in W:
fs.append({"sy": 5, "sx": 5, "depth": W.shape[3], "w": ['%.2f' % elem for elem in list(w_.flatten())]})
lines += """
var layer_%s = {
"layer_type": "deconv",
"sy": 5, "sx": 5,
"out_sx": %s, "out_sy": %s,
"stride": 2, "pad": 1,
"out_depth": %s, "in_depth": %s,
"biases": %s,
"gamma": %s,
"beta": %s,
"filters": %s
};""" % (layer_idx, 2**(int(layer_idx)+2), 2**(int(layer_idx)+2),
W.shape[0], W.shape[3], biases, gamma, beta, fs)
layer_f.write(" ".join(lines.replace("'","").split()))
def make_gif(images, fname, duration=2, true_image=False):
import moviepy.editor as mpy
def make_frame(t):
try:
x = images[int(len(images)/duration*t)]
except:
x = images[-1]
if true_image:
return x.astype(np.uint8)
else:
return ((x+1)/2*255).astype(np.uint8)
clip = mpy.VideoClip(make_frame, duration=duration)
clip.write_gif(fname, fps = len(images) / duration)
def visualize(sess, dcgan, config, option, sample_dir='samples'):
image_frame_dim = int(math.ceil(config.batch_size**.5))
if option == 0:
z_sample = np.random.uniform(-0.5, 0.5, size=(config.batch_size, dcgan.z_dim))
samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
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() )))
elif option == 1:
values = np.arange(0, 1, 1./config.batch_size)
for idx in xrange(dcgan.z_dim):
print(" [*] %d" % idx)
z_sample = np.random.uniform(-1, 1, size=(config.batch_size , dcgan.z_dim))
for kdx, z in enumerate(z_sample):
z[idx] = values[kdx]
if config.dataset == "mnist":
y = np.random.choice(10, config.batch_size)
y_one_hot = np.zeros((config.batch_size, 10))
y_one_hot[np.arange(config.batch_size), y] = 1
samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample, dcgan.y: y_one_hot})
else:
samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
save_images(samples, [image_frame_dim, image_frame_dim], os.path.join(sample_dir, 'test_arange_%s.png' % (idx)))
elif option == 2:
values = np.arange(0, 1, 1./config.batch_size)
for idx in [random.randint(0, dcgan.z_dim - 1) for _ in xrange(dcgan.z_dim)]:
print(" [*] %d" % idx)
z = np.random.uniform(-0.2, 0.2, size=(dcgan.z_dim))
z_sample = np.tile(z, (config.batch_size, 1))
#z_sample = np.zeros([config.batch_size, dcgan.z_dim])
for kdx, z in enumerate(z_sample):
z[idx] = values[kdx]
if config.dataset == "mnist":
y = np.random.choice(10, config.batch_size)
y_one_hot = np.zeros((config.batch_size, 10))
y_one_hot[np.arange(config.batch_size), y] = 1
samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample, dcgan.y: y_one_hot})
else:
samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
try:
make_gif(samples, './samples/test_gif_%s.gif' % (idx))
except:
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() )))
elif option == 3:
values = np.arange(0, 1, 1./config.batch_size)
for idx in xrange(dcgan.z_dim):
print(" [*] %d" % idx)
z_sample = np.zeros([config.batch_size, dcgan.z_dim])
for kdx, z in enumerate(z_sample):
z[idx] = values[kdx]
samples = sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample})
make_gif(samples, os.path.join(sample_dir, 'test_gif_%s.gif' % (idx)))
elif option == 4:
image_set = []
values = np.arange(0, 1, 1./config.batch_size)
for idx in xrange(dcgan.z_dim):
print(" [*] %d" % idx)
z_sample = np.zeros([config.batch_size, dcgan.z_dim])
for kdx, z in enumerate(z_sample): z[idx] = values[kdx]
image_set.append(sess.run(dcgan.sampler, feed_dict={dcgan.z: z_sample}))
make_gif(image_set[-1], os.path.join(sample_dir, 'test_gif_%s.gif' % (idx)))
new_image_set = [merge(np.array([images[idx] for images in image_set]), [10, 10]) \
for idx in range(64) + range(63, -1, -1)]
make_gif(new_image_set, './samples/test_gif_merged.gif', duration=8)
def image_manifold_size(num_images):
manifold_h = int(np.floor(np.sqrt(num_images)))
manifold_w = int(np.ceil(np.sqrt(num_images)))
assert manifold_h * manifold_w == num_images
return manifold_h, manifold_w
================================================
FILE: web/app.py
================================================
from flask import Flask
from flask import render_template
app = Flask(__name__, template_folder="./", static_folder='./', static_url_path='')
@app.route('/')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.debug=True
app.run(host='0.0.0.0')
================================================
FILE: web/css/fakeLoader.css
================================================
/**********************
*CSS Animations by:
*http://codepen.io/vivinantony
***********************/
.spinner1 {
width: 40px;
height: 40px;
position: relative;
}
.double-bounce1, .double-bounce2 {
width: 100%;
height: 100%;
border-radius: 50%;
background-color: #fff;
opacity: 0.6;
position: absolute;
top: 0;
left: 0;
-webkit-animation: bounce 2.0s infinite ease-in-out;
animation: bounce 2.0s infinite ease-in-out;
}
.double-bounce2 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
@-webkit-keyframes bounce {
0%, 100% { -webkit-transform: scale(0.0) }
50% { -webkit-transform: scale(1.0) }
}
@keyframes bounce {
0%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
} 50% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}
.spinner2 {
width: 40px;
height: 40px;
position: relative;
}
.container1 > div, .container2 > div, .container3 > div {
width: 6px;
height: 6px;
background-color: #fff;
border-radius: 100%;
position: absolute;
-webkit-animation: bouncedelay 1.2s infinite ease-in-out;
animation: bouncedelay 1.2s infinite ease-in-out;
/* Prevent first frame from flickering when animation starts */
-webkit-animation-fill-mode: both;
animation-fill-mode: both;
}
.spinner2 .spinner-container {
position: absolute;
width: 100%;
height: 100%;
}
.container2 {
-webkit-transform: rotateZ(45deg);
transform: rotateZ(45deg);
}
.container3 {
-webkit-transform: rotateZ(90deg);
transform: rotateZ(90deg);
}
.circle1 { top: 0; left: 0; }
.circle2 { top: 0; right: 0; }
.circle3 { right: 0; bottom: 0; }
.circle4 { left: 0; bottom: 0; }
.container2 .circle1 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s;
}
.container3 .circle1 {
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
.container1 .circle2 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s;
}
.container2 .circle2 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s;
}
.container3 .circle2 {
-webkit-animation-delay: -0.7s;
animation-delay: -0.7s;
}
.container1 .circle3 {
-webkit-animation-delay: -0.6s;
animation-delay: -0.6s;
}
.container2 .circle3 {
-webkit-animation-delay: -0.5s;
animation-delay: -0.5s;
}
.container3 .circle3 {
-webkit-animation-delay: -0.4s;
animation-delay: -0.4s;
}
.container1 .circle4 {
-webkit-animation-delay: -0.3s;
animation-delay: -0.3s;
}
.container2 .circle4 {
-webkit-animation-delay: -0.2s;
animation-delay: -0.2s;
}
.container3 .circle4 {
-webkit-animation-delay: -0.1s;
animation-delay: -0.1s;
}
@-webkit-keyframes bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0.0) }
40% { -webkit-transform: scale(1.0) }
}
@keyframes bouncedelay {
0%, 80%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
} 40% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}
.spinner3 {
width: 40px;
height: 40px;
position: relative;
-webkit-animation: rotate 2.0s infinite linear;
animation: rotate 2.0s infinite linear;
}
.dot1, .dot2 {
width: 60%;
height: 60%;
display: inline-block;
position: absolute;
top: 0;
background-color: #fff;
border-radius: 100%;
-webkit-animation: bounce 2.0s infinite ease-in-out;
animation: bounce 2.0s infinite ease-in-out;
}
.dot2 {
top: auto;
bottom: 0px;
-webkit-animation-delay: -1.0s;
animation-delay: -1.0s;
}
@-webkit-keyframes rotate { 100% { -webkit-transform: rotate(360deg) }}
@keyframes rotate { 100% { transform: rotate(360deg); -webkit-transform: rotate(360deg) }}
@-webkit-keyframes bounce {
0%, 100% { -webkit-transform: scale(0.0) }
50% { -webkit-transform: scale(1.0) }
}
@keyframes bounce {
0%, 100% {
transform: scale(0.0);
-webkit-transform: scale(0.0);
} 50% {
transform: scale(1.0);
-webkit-transform: scale(1.0);
}
}
.spinner4 {
width: 30px;
height: 30px;
background-color: #fff;
-webkit-animation: rotateplane 1.2s infinite ease-in-out;
animation: rotateplane 1.2s infinite ease-in-out;
}
@-webkit-keyframes rotateplane {
0% { -webkit-transform: perspective(120px) }
50% { -webkit-transform: perspective(120px) rotateY(180deg) }
100% { -webkit-transform: perspective(120px) rotateY(180deg) rotateX(180deg) }
}
@keyframes rotateplane {
0% {
transform: perspective(120px) rotateX(0deg) rotateY(0deg);
-webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg)
} 50% {
transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg);
-webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg)
} 100% {
transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
-webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg);
}
}
@charset 'UTF-8';
/* Slider */
.slick-loading .slick-list
{
background: #fff url('./ajax-loader.gif') center center no-repeat;
}
/* Icons */
@font-face
{
font-family: 'slick';
font-weight: normal;
font-style: normal;
src: url('../fonts/slick.eot');
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');
}
/* Arrows */
.slick-prev,
.slick-next
{
font-size: 0;
line-height: 0;
position: absolute;
top: 50%;
display: block;
width: 20px;
height: 20px;
margin-top: -10px;
padding: 0;
cursor: pointer;
color: transparent;
border: none;
outline: none;
background: transparent;
}
.slick-prev:hover,
.slick-prev:focus,
.slick-next:hover,
.slick-next:focus
{
color: transparent;
outline: none;
background: transparent;
}
.slick-prev:hover:before,
.slick-prev:focus:before,
.slick-next:hover:before,
.slick-next:focus:before
{
opacity: 1;
}
.slick-prev.slick-disabled:before,
.slick-next.slick-disabled:before
{
opacity: .25;
}
.slick-prev:before,
.slick-next:before
{
font-family: 'slick';
font-size: 20px;
line-height: 1;
opacity: .75;
color: white;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.slick-prev
{
left: -25px;
}
[dir='rtl'] .slick-prev
{
right: -25px;
left: auto;
}
.slick-prev:before
{
content: '←';
}
[dir='rtl'] .slick-prev:before
{
content: '→';
}
.slick-next
{
right: -25px;
}
[dir='rtl'] .slick-next
{
right: auto;
left: -25px;
}
.slick-next:before
{
content: '→';
}
[dir='rtl'] .slick-next:before
{
content: '←';
}
/* Dots */
.slick-slider
{
margin-bottom: 30px;
}
.slick-dots
{
position: absolute;
bottom: -45px;
display: block;
width: 100%;
padding: 0;
list-style: none;
text-align: center;
}
.slick-dots li
{
position: relative;
display: inline-block;
width: 20px;
height: 20px;
margin: 0 5px;
padding: 0;
cursor: pointer;
}
.slick-dots li button
{
font-size: 0;
line-height: 0;
display: block;
width: 20px;
height: 20px;
padding: 5px;
cursor: pointer;
color: transparent;
border: 0;
outline: none;
background: transparent;
}
.slick-dots li button:hover,
.slick-dots li button:focus
{
outline: none;
}
.slick-dots li button:hover:before,
.slick-dots li button:focus:before
{
opacity: 1;
}
.slick-dots li button:before
{
font-family: 'slick';
font-size: 6px;
line-height: 20px;
position: absolute;
top: 0;
left: 0;
width: 20px;
height: 20px;
content: '•';
text-align: center;
opacity: .25;
color: white;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.slick-dots li.slick-active button:before
{
opacity: .75;
color: white;
}
================================================
FILE: web/css/main.css
================================================
canvas {
background-color: white;
}
#colors {
overflow: hidden;
width: 90px;
margin-left: 30px;
float: left;
}
#colors .color {
float: left;
width: 40px;
height: 40px;
}
#colors .color div {
width: 100%;
height: 100%;
cursor: pointer;
}
#colors .color #color_1::before { background-color: #000000; }
#colors .color #color_1::after { background-color: #000000; }
#colors .color #color_2::before { background-color: #444444; }
#colors .color #color_2::after { background-color: #303030; }
#colors .color #color_3::before { background-color: #000088; }
#colors .color #color_3::after { background-color: #00005f; }
#colors .color #color_4::before { background-color: #1111ff; }
#colors .color #color_4::after { background-color: #0000be; }
#colors .color #color_5::before { background-color: #008800; }
#colors .color #color_5::after { background-color: #005f00; }
#colors .color #color_6::before { background-color: #11ff11; }
#colors .color #color_6::after { background-color: #00be00; }
#colors .color #color_7::before { background-color: #008888; }
#colors .color #color_7::after { background-color: #005f5f; }
#colors .color #color_8::before { background-color: #11ffff; }
#colors .color #color_8::after { background-color: #00bebe; }
#colors .color #color_9::before { background-color: #880000; }
#colors .color #color_9::after { background-color: #5f0000; }
#colors .color #color_10::before { background-color: #ff1111; }
#colors .color #color_10::after { background-color: #be0000; }
#colors .color #color_11::before { background-color: #880088; }
#colors .color #color_11::after { background-color: #5f005f; }
#colors .color #color_12::before { background-color: #ff11ff; }
#colors .color #color_12::after { background-color: #be00be; }
#colors .color #color_13::before { background-color: #884400; }
#colors .color #color_13::after { background-color: #5f3000; }
#colors .color #color_14::before { background-color: #ffff11; }
#colors .color #color_14::after { background-color: #bebe00; }
#colors .color #color_15::before { background-color: #888888; }
#colors .color #color_15::after { background-color: #5f5f5f; }
#colors .color #color_16::before { background-color: #cccccc; }
#colors .color #color_16::after { background-color: #8f8f8f; }
#colors .color .isometric::after {
width: 40px;
height: 10px;
}
#colors .color .isometric::before {
width: 10px;
height: 40px;
}
#colors .color.active div {
top: 10px;
left: 10px;
}
/* CANVAS */
#canvas {
float: left;
cursor: crosshair;
width: 320px;
height: 320px;
margin: 0 auto;
}
#canvas.isometric::after {
width: 320px;
height: 10px;
}
#canvas.isometric::before {
width: 10px;
height: 320px;
}
/*!
* Start Bootstrap - Grayscale Bootstrap Theme (http://startbootstrap.com)
* Code licensed under the Apache License v2.0.
* For details, see http://www.apache.org/licenses/LICENSE-2.0.
*/
body {
width: 100%;
height: 100%;
font-family: Lora,"Helvetica Neue",Helvetica,Arial,sans-serif;
color: #fff;
background-color: #000;
}
html {
width: 100%;
height: 100%;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0 0 35px;
text-transform: uppercase;
font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif;
font-weight: 700;
letter-spacing: 1px;
}
p {
margin: 0 0 25px;
font-size: 18px;
line-height: 1.5;
}
@media(min-width:768px) {
p {
margin: 0 0 35px;
font-size: 20px;
line-height: 1.6;
}
}
b {
color: #3A8BB2;
}
a {
color: #42dca3;
-webkit-transition: all .2s ease-in-out;
-moz-transition: all .2s ease-in-out;
transition: all .2s ease-in-out;
}
a:hover,
a:focus {
text-decoration: none;
color: #1d9b6c;
}
.light {
font-weight: 400;
}
.navbar-custom {
margin-bottom: 0;
border-bottom: 1px solid rgba(255,255,255,.3);
text-transform: uppercase;
font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif;
background-color: #000;
}
.navbar-custom .navbar-brand {
font-weight: 700;
}
.navbar-custom .navbar-brand:focus {
outline: 0;
}
.navbar-custom .navbar-brand .navbar-toggle {
padding: 4px 6px;
font-size: 16px;
color: #fff;
}
.navbar-custom .navbar-brand .navbar-toggle:focus,
.navbar-custom .navbar-brand .navbar-toggle:active {
outline: 0;
}
.navbar-custom a {
color: #fff;
}
.navbar-custom .nav li a {
-webkit-transition: background .3s ease-in-out;
-moz-transition: background .3s ease-in-out;
transition: background .3s ease-in-out;
}
.navbar-custom .nav li a:hover {
outline: 0;
color: rgba(255,255,255,.8);
background-color: transparent;
}
.navbar-custom .nav li a:focus,
.navbar-custom .nav li a:active {
outline: 0;
background-color: transparent;
}
.navbar-custom .nav li.active {
outline: 0;
}
.navbar-custom .nav li.active a {
background-color: rgba(255,255,255,.3);
}
.navbar-custom .nav li.active a:hover {
color: #fff;
}
@media(min-width:768px) {
.navbar-custom {
padding: 20px 0;
border-bottom: 0;
letter-spacing: 1px;
background: 0 0;
-webkit-transition: background .5s ease-in-out,padding .5s ease-in-out;
-moz-transition: background .5s ease-in-out,padding .5s ease-in-out;
transition: background .5s ease-in-out,padding .5s ease-in-out;
}
.navbar-custom.top-nav-collapse {
padding: 0;
border-bottom: 1px solid rgba(255,255,255,.3);
background: #000;
}
}
#background {
position: absolute;
top: 0;
left: 0;
min-width: 100%;
min-height: 100%;
width: auto;
height: auto;
}
.intro {
overflow: hidden;
position: relative;
display: table;
width: 100%;
height: auto;
min-height: 100%;
padding: 100px 0;
text-align: center;
color: #fff;
background-color: #000;
-webkit-background-size: cover;
-moz-background-size: cover;
background-size: cover;
-o-background-size: cover;
}
.intro .intro-body {
display: table-cell;
vertical-align: middle;
}
.intro .intro-body .brand-heading {
font-size: 40px;
}
.intro .intro-body .intro-text {
font-size: 18px;
}
@media(min-width:768px) {
.intro {
height: 100%;
padding: 0;
}
.intro .intro-body .brand-heading {
font-size: 100px;
}
.intro .intro-body .intro-text {
font-size: 26px;
}
}
.btn-circle {
width: 70px;
height: 70px;
margin-top: 15px;
padding: 7px 16px;
border: 2px solid #fff;
border-radius: 100%!important;
font-size: 40px;
color: #fff;
background: 0 0;
-webkit-transition: background .3s ease-in-out;
-moz-transition: background .3s ease-in-out;
transition: background .3s ease-in-out;
}
.btn-circle:hover,
.btn-circle:focus {
outline: 0;
color: #fff;
background: rgba(255,255,255,.1);
}
.btn-circle i.animated {
-webkit-transition-property: -webkit-transform;
-webkit-transition-duration: 1s;
-moz-transition-property: -moz-transform;
-moz-transition-duration: 1s;
}
.btn-circle:hover i.animated {
-webkit-animation-name: pulse;
-moz-animation-name: pulse;
-webkit-animation-duration: 1.5s;
-moz-animation-duration: 1.5s;
-webkit-animation-iteration-count: infinite;
-moz-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
-moz-animation-timing-function: linear;
}
@-webkit-keyframes pulse {
0% {
-webkit-transform: scale(1);
transform: scale(1);
}
50% {
-webkit-transform: scale(1.2);
transform: scale(1.2);
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
}
}
@-moz-keyframes pulse {
0% {
-moz-transform: scale(1);
transform: scale(1);
}
50% {
-moz-transform: scale(1.2);
transform: scale(1.2);
}
100% {
-moz-transform: scale(1);
transform: scale(1);
}
}
.content-section {
padding-top: 100px;
}
.download-section {
width: 100%;
padding: 50px 0;
color: #fff;
background: url(../img/downloads-bg.jpg) no-repeat center center scroll;
background-color: #000;
-webkit-background-size: cover;
-moz-background-size: cover;
background-size: cover;
-o-background-size: cover;
}
@media(min-width:767px) {
.content-section {
padding-top: 250px;
}
.download-section {
padding: 100px 0;
}
}
.btn {
border-radius: 0;
text-transform: uppercase;
font-family: Montserrat,"Helvetica Neue",Helvetica,Arial,sans-serif;
font-weight: 400;
-webkit-transition: all .3s ease-in-out;
-moz-transition: all .3s ease-in-out;
transition: all .3s ease-in-out;
}
.btn-default {
border: 1px solid #42dca3;
color: #42dca3;
background-color: transparent;
}
.btn-default:hover,
.btn-default:focus {
border: 1px solid #42dca3;
outline: 0;
color: #000;
background-color: #42dca3;
}
ul.banner-social-buttons {
margin-top: 0;
}
@media(max-width:1199px) {
ul.banner-social-buttons {
margin-top: 15px;
}
}
@media(max-width:767px) {
ul.banner-social-buttons li {
display: block;
margin-bottom: 20px;
padding: 0;
}
ul.banner-social-buttons li:last-child {
margin-bottom: 0;
}
}
footer {
padding: 50px 0;
}
footer p {
margin: 0;
}
::-moz-selection {
text-shadow: none;
background: #fcfcfc;
background: rgba(255,255,255,.2);
}
::selection {
text-shadow: none;
background: #fcfcfc;
background: rgba(255,255,255,.2);
}
img::selection {
background: 0 0;
}
img::-moz-selection {
background: 0 0;
}
body {
webkit-tap-highlight-color: rgba(255,255,255,.2);
}
.logo {
background-color: rgba(0,0,0,0.7);
padding: 30px;
}
.tooltip > .tooltip-inner { background-color: white; color: black; }
.tooltip > .tooltip-arrow { border-bottom-color: white; color: black; }
.tooltip {
opacity: 0.95 !important;
}
.pixel-tooltip {
padding: 0px !important;
background-color: transparent !important;
}
.fa-5 {
font-size: 40px !important;
}
.ico {
padding-right: 10px;
}
.sk-wave {
margin: 40px auto;
width: 50px;
height: 40px;
text-align: center;
font-size: 10px; }
.sk-wave .sk-rect {
background-color: #eeeeee;
height: 100%;
width: 6px;
display: inline-block;
-webkit-animation: sk-waveStretchDelay 1.2s infinite ease-in-out;
animation: sk-waveStretchDelay 1.2s infinite ease-in-out; }
.sk-wave .sk-rect1 {
-webkit-animation-delay: -1.2s;
animation-delay: -1.2s; }
.sk-wave .sk-rect2 {
-webkit-animation-delay: -1.1s;
animation-delay: -1.1s; }
.sk-wave .sk-rect3 {
-webkit-animation-delay: -1s;
animation-delay: -1s; }
.sk-wave .sk-rect4 {
-webkit-animation-delay: -0.9s;
animation-delay: -0.9s; }
.sk-wave .sk-rect5 {
-webkit-animation-delay: -0.8s;
animation-delay: -0.8s; }
@-webkit-keyframes sk-waveStretchDelay {
0%, 40%, 100% {
-webkit-transform: scaleY(0.4);
transform: scaleY(0.4); }
20% {
-webkit-transform: scaleY(1);
transform: scaleY(1); } }
@keyframes sk-waveStretchDelay {
0%, 40%, 100% {
-webkit-transform: scaleY(0.4);
transform: scaleY(0.4); }
20% {
-webkit-transform: scaleY(1);
transform: scaleY(1); } }
================================================
FILE: web/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="author" content="Taehoon Kim (carpedm20@gmail.com)">
<meta name="keywords" content="프사 뉴럴,neural face,dcgan,carpedm20" />
<meta name="description" content="Face from AI | 인공지능이 만든 얼굴들" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta property="og:title" content="Neural Face | 프사 뉴럴" />
<meta property="og:url" content="http://carpedm20.github.io/faces/"/>
<meta property="og:image" content="https://raw.githubusercontent.com/carpedm20/DCGAN-tensorflow/master/web/img/ogimage.png" />
<meta content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport">
<style type="text/css">
body.ko :lang(en) {
display: none;
}
body.en :lang(ko) {
display: none;
}
</style>
<title lang="en">Neural Face | 프사 뉴럴</title>
<!-- CSS -->
<link rel="stylesheet" type="text/css" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<link rel="stylesheet" type="text/css" href="//kenwheeler.github.io/slick/slick/slick.css"/>
<link rel="stylesheet" type="text/css" href="//kenwheeler.github.io/slick/slick/slick-theme.css"/>
<link href="css/fakeLoader.css" rel="stylesheet">
<link href="css/main.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic|Montserrat:400,700" rel="stylesheet" type="text/css">
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
</head>
<body id="page-top" data-spy="scroll" data-target=".navbar-fixed-top" class="en">
<div id="fakeLoader"></div>
<div id="fb-root"></div>
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.5&appId=776839839027483";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<!-- Navigation -->
<nav class="navbar navbar-custom navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-main-collapse">
<i class="fa fa-bars"></i>
</button>
<a class="navbar-brand page-scroll" href="http://carpedm20.github.io/" target="_blank">
<span class="light">carpedm20</span>
</a>
</div>
<div class="collapse navbar-collapse navbar-right navbar-main-collapse">
<ul class="nav navbar-nav">
<li class="hidden">
<a href="#page-top"></a>
</li>
<li>
<a class="page-scroll" href="#about">About</a>
</li>
<li>
<a class="page-scroll" href="#generate">Generate</a>
</li>
<li>
<a class="page-scroll" href="#details">Details</a>
</li>
<li>
<a class="page-scroll" href="#results">Results</a>
</li>
<li>
<a class="page-scroll" href="#aboutme">Me?</a>
</li>
</ul>
</div>
</div>
</nav>
<!-- Intro Header -->
<header class="intro">
<video autoplay loop muted id="background">
<source src="videos/background.mp4" type="video/mp4">
</video>
<div class="intro-body">
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2 col-xs-12 logo">
<h1 lang="ko" class="brand-heading">프사 뉴럴</h1>
<h2 lang="en" class="brand-heading">Neural Face</h2>
<p lang="ko" class="intro-text">인공지능이 만든 얼굴들</p>
<div class="row">
<button class="btn-default" onclick="document.body.className='en'">English</button>
<button class="btn-default" onclick="document.body.className='ko'">한국어</button>
</div>
<a href="#about" class="btn btn-circle page-scroll">
<i class="fa fa-angle-double-down animated"></i>
</a>
</div>
</div>
</div>
</div>
</header>
<section id="about" class="container content-section text-center">
<div class="row">
<h2 lang="ko">프사 뉴럴</h2>
<h2 lang="en">Neural Face</h2>
<div lang="ko" class="col-md-8 col-md-offset-2 col-xs-12">
<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>
<p><b>프사 뉴럴</b>은 얼굴 사진을 만드는 인공 지능이며<br>이 페이지에 나오는 모든 사람들은 이세상에 존재하지 않습니다.</p>
</div>
<div lang="en" class="col-md-8 col-md-offset-2 col-xs-12">
<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>
<p><b>Neural Face</b> is an Artificial Intelligence which generates face images<br>and all images in this page are not REAL.</p>
</div>
<div class="col-md-8 col-md-offset-2 col-xs-12">
<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>
</div>
</div>
</section>
<section id="generate" class="container content-section text-center">
<div class="row">
<h2>Image Generation</h2>
<div class="col-md-8 col-md-offset-2">
<img src="img/single1.gif">
<img src="img/single2.gif">
<img src="img/single3.gif">
<img src="img/single4.gif">
</div>
<div lang="ko" class="col-md-8 col-md-offset-2 col-xs-12 text-left">
<br/><p><b>프사 뉴럴</b>은 0에서 1 사이의 100개의 숫자 <b>z</b>로 사람의 이미지를 만들어내는 인공지능입니다.</p>
<p>1. 아래에 보이는 100개의 픽셀을 <b>z</b>의 각 숫자를 나타냅니다.<br/>2. 만들어진 사진 위에 마우스를 올리면 사진에 사용된 <b>z</b>가 보입니다.<br/>3. 만들어진 이미지를 누르시면 그 이미지의 <b>z</b>가 복사됩니다.</p>
</div>
<div lang="en" class="col-md-8 col-md-offset-2 col-xs-12 text-left">
<br/><p><b>Neural Face</b> uses a vector <b>z</b> that consists of 100 real numbers ranging from 0 to 1.</p>
<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>
</div>
<div class="col-md-8 col-md-offset-2 col-xs-12">
<p lang="ko"><small>(브라우저 성능에 따라 1~10초가 걸립니다)</small></p>
<p lang="en"><small>(Might take 1 to 10 seconds depending on your browser)</small></p>
<div class="row" id="images">
</div>
<div class="row">
<br/>
<div class="col-md-7 col-xs-8">
<canvas id="pixel" height='200' width='200' style="float:right" onselectstart="this.style.cursor='crosshair'; return false;">
<p>
Sorry, your browser doesn't support the <canvas> element.
</p>
<p>
Please upgrade to
<a href='http://www.microsoft.com/IE9' target="_blank">IE 9</a>
or use the latest
<a href='http://www.google.com/chrome' target="_blank">Chrome</a>
or
<a href='http://www.mozilla.com/' target="_blank">Firefox</a>
</p>
</canvas>
</div>
<div class="col-xs-5" id="colors">
<div class="color" data-color="#000000">
<div class="isometric" id="color_1" style="background-color: #000000"></div>
</div>
<div class="color" data-color="#444444">
<div class="isometric" id="color_2" style="background-color: #444444"></div>
</div>
<div class="color" data-color="#888888">
<div class="isometric" id="color_3" style="background-color: #888888"></div>
</div>
<div class="color" data-color="#CCCCCC">
<div class="isometric" id="color_4" style="background-color: #CCCCCC"></div>
</div>
<div class="color" data-color="#FFFFFF">
<div class="isometric" id="color_5" style="background-color: #FFFFFF"></div>
</div>
</div>
</div>
<div id="loading" class="row">
<div class="sk-wave">
<div class="sk-rect sk-rect1"></div>
<div class="sk-rect sk-rect2"></div>
<div class="sk-rect sk-rect3"></div>
<div class="sk-rect sk-rect4"></div>
<div class="sk-rect sk-rect5"></div>
</div>
<div class="col-xs-12 text-center">
<p lang="ko"><b>프사 뉴럴</b>을 불러오고 있습니다...</p>
<p lang="en"><b>Neural Face</b> is preparing to draw...</p>
</div>
</div>
<div id="draw-btn" class="row" style="display: none;">
<br/>
<button lang="ko" type="button" class="btn btn-default draw" class="btn btn-default">만들기</button>
<button lang="ko" type="button" class="btn btn-default shuffle" class="btn btn-default">랜덤 얼굴</button>
<button lang="en" type="button" class="btn btn-default draw" class="btn btn-default">Generate</button>
<button lang="en" type="button" class="btn btn-default shuffle" class="btn btn-default">Random face</button>
</div>
</div>
</div>
</section>
<section id="details" class="container content-section text-center">
<div class="row">
<div class="col-md-8 col-md-offset-2 col-xs-12">
<h2 lang="ko">알고리즘</h2>
<h2 lang="en">Algorithm</h2>
</div>
<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">
<div lang="ko" class="col-md-8 col-md-offset-2 col-xs-12 text-left">
<p><b>프사 뉴럴</b>의 핵심 모델인 <a href="http://arxiv.org/pdf/1511.06434v2.pdf" target="_blank">DCGAN</a>은 두 개의 인공 신경망으로 구성되어 있으며, 각각</p>
<p>1. 사진을 만들어내는 <b>생성자 (G)</b><br/>2. 진짜 사진과 생성자가 만든 사진을 구분하는 <b>구분자 (D)</b></p>
<p>라고 부릅니다.</p>
<p>두 신경망은 수많은 이미지를 반복적으로 보면서 생성자는 구분자를 속이기 위해, 구분자는 생성자가 만든 사진을 판별하기 위해 학습합니다. 이러한 학습 방법을 <a href="https://en.wikipedia.org/wiki/Adversarial_machine_learning">적대적 학습 (Adversarial Learning)</a>이라고 하며, 생성자와 구분자를 <b>도둑</b>과 <b>경찰</b>로 비유하기도 합니다.</p>
</div>
<div lang="en" class="col-md-8 col-md-offset-2 col-xs-12 text-left">
<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>
<p>1. <b>Generator (G)</b> that generates an image<br/>2. <b>Discriminator (D)</b> that discriminate real images from generated images</p>
<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>
</div>
<br/>
<img class="col-md-6 col-md-offset-3 col-xs-10 col-xs-offset-1" src="img/model.png">
<div lang="ko" class="col-md-8 col-md-offset-2 col-xs-12 text-left">
<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>
<p>구분자는 인공 신경망에 실제 이미지를 넣은 결과를 <b>1</b>로, 만들어진 이미지의 결과는 <b>0</b>으로 구분하도록 학습합니다. 반대로 생성자는 Gaussian Distribution을 따르는 <b>z</b>라는 확률 변수를 두고, 사람의 이미지의 확률 분포를 <b>z</b>를 사용해 계산합니다. 이렇게 만들어진 이미지를 구분자가 실제 이미지라고 잘못 판단하도록 계속 학습합니다.</p>
</div>
<div lang="en" class="col-md-8 col-md-offset-2 col-xs-12 text-left">
<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>
<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>
</div>
</div>
</section>
<section id="results" class="container content-section text-center">
<div class="row">
<h2>Results</h2>
<div class="col-md-8 col-md-offset-2 col-xs-12 text-left">
<p lang="ko"><b>프사 뉴럴</b>를 학습시키기 위해 인터넷에 10만 개 이상의 사진들을 모았고 이 사진들에서 얼굴 사진만 잘라서 얼굴 데이터 셋을 만들었습니다. 코드는 최근에 구글에서 공개한 <a href="https://www.tensorflow.org/" target="_blank">TensorFlow</a>로 구현했으며 GTX 980 Ti를 사용하여 이틀간 학습시켰습니다.</p>
<p lang="ko">아래는 초기 학습 단계에서 프사 뉴럴이 정해진 <b>z</b>로 얼굴 사진을 만들어 가는 과정을 보여줍니다.</p>
<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>
<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>
<video autoplay loop muted class="col-md-6 col-md-offset-3 col-xs-10 col-xs-offset-1">
<source src="videos/training.mp4" type="video/mp4">
</video>
</div>
<div class="col-md-8 col-md-offset-2 col-xs-12 text-left">
<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>
<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>
</div>
<div class="col-md-8 col-md-offset-2 col-xs-12">
<img src="img/average.png"/>
</div>
<div class="col-md-8 col-md-offset-2 col-xs-12 text-left">
<p lang="ko"><br/><br/>평균값 <b>0</b>에서 랜덤한 차원의 값을 조금씩 바꾸면 아래와 같은 변화를 볼 수 있습니다.</p>
<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>
</div>
<div class="col-md-8 col-md-offset-2 col-xs-12">
<video autoplay loop muted class="col-md-6 col-md-offset-3 col-xs-10 col-xs-offset-1">
<source src="videos/random.mp4" type="video/mp4">
</video>
</div>
<div class="col-md-8 col-md-offset-2 col-xs-12 text-left">
<p lang="ko"><br/><br/>아래의 사진들은 100차원의 <b>z</b> 값 중에서 임의의 차원들을 -1부터 1까지 바꾸면서 <b>생성자</b> 신경망에 넣은 결과이며, 점점 미소를 짓거나, 안경이 생기거나, 흑백 사진이 되거나, 성별이 바뀌는 등의 결과를 확인하실 수 있습니다.</p>
<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>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2 col-xs-12">
<div class="slick">
<img src="img/change1.png"/>
<img src="img/change2.png"/>
<img src="img/change3.png"/>
<img src="img/change4.png"/>
<img src="img/change5.png"/>
<img src="img/change6.png"/>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2 col-xs-12">
<p lang="ko"><br><br><b>프사 뉴럴</b>의 코드는 <a href="https://github.com/carpedm20/DCGAN-tensorflow" target="_blank">이곳</a>에 공개되어 있습니다.</p>
<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>
</div>
</div>
</section>
<section id="results" class="container content-section text-center">
<div class="row">
<h2>Misc.</h2>
<div lang="ko" class="col-md-8 col-md-offset-2 col-xs-12">
<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>
<p><small>(마우스로 클릭하면 정답이 보입니다)</small></p>
</div>
<div lang="en" class="col-md-8 col-md-offset-2 col-xs-12">
<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>
<p><small>(Answer will be showed if you click an image)</small></p>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2 col-xs-12">
<div class="turing-slick">
<img data-container="body" data-toggle="tooltip" title="true." src="img/t1.jpg"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f1.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f2.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f3.png"/>
<img data-container="body" data-toggle="tooltip" title="true." src="img/t6.jpg"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f4.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f5.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f6.png"/>
<img data-container="body" data-toggle="tooltip" title="true." src="img/t2.jpg"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f7.png"/>
<img data-container="body" data-toggle="tooltip" title="true." src="img/t7.jpg"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f8.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f9.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f17.png"/>
<img data-container="body" data-toggle="tooltip" title="true." src="img/t3.jpg"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f10.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f11.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f12.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f13.png"/>
<img data-container="body" data-toggle="tooltip" title="true." src="img/t4.jpg"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f14.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f15.png"/>
<img data-container="body" data-toggle="tooltip" title="fake!" src="img/f16.png"/>
<img data-container="body" data-toggle="tooltip" title="true." src="img/t5.jpg"/>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8 col-md-offset-2 col-xs-12">
<br/></br/>
<br/></br/>
<br/></br/>
<h2>Other Projects</h2>
<ul style="list-style-type:none; padding: 0; font-size: 20px;">
<li><a href="https://github.com/carpedm20/MemN2N-tensorflow" target="_blank">Memory Networks</a></li>
<li><a href="https://github.com/carpedm20/NTM-tensorflow" target="_blank">Neural Turing Machine</a></li>
<li><a href="https://github.com/carpedm20/lstm-char-cnn-tensorflow" target="_blank">LSTM Character CNN Networks</a></li>
</ul>
</div>
</div>
</section>
<!-- Contact Section -->
<section id="aboutme" class="container content-section text-center">
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<h3>Taehoon Kim</h3>
<h3><a href="http://carpedm20.github.io/" target="_blank">@carpedm20</a></h3>
<a class="ico" href="https://github.com/carpedm20/" target="_blank">
<i class="fa fa-github-square fa-5"></i>
</a>
<a class="ico" href="https://www.linkedin.com/in/carpedm20" target="_blank">
<i class="fa fa-linkedin-square fa-5"></i>
</a>
<a class="ico" href="https://www.facebook.com/carpedm20" target="_blank">
<i class="fa fa-facebook-square fa-5"></i>
</a>
<a href="https://twitter.com/carpedm20" target="_blank">
<i class="fa fa-twitter-square fa-5"></i>
</a>
</div>
</div>
</section>
<!-- Footer -->
<footer>
<div class="container content-section text-center">
</div>
</footer>
<!-- jQuery -->
<script src="//code.jquery.com/jquery-2.2.0.min.js"></script>
<script src="js/vendor/fakeLoader.min.js"></script>
<script>
$("#fakeLoader").fakeLoader({
zIndex:"999",//Default zIndex
spinner:"spinner3",
bgColor:"black"
})
</script>
<script type="text/javascript" src="https://kenwheeler.github.io/slick/slick/slick.js"></script>
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
<script src="js/vendor/jquery.easing.min.js"></script>
<script src="js/vendor/pixel.min.js"></script>
<script src="js/convnet.js"></script>
<script src="js/layers.js"></script>
<script src="js/app.js"></script>
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
<script>
(function(b,o,i,l,e,r){b.GoogleAnalyticsObject=l;b[l]||(b[l]=
function(){(b[l].q=b[l].q||[]).push(arguments)});b[l].l=+new Date;
e=o.createElement(i);r=o.getElementsByTagName(i)[0];
e.src='https://www.google-analytics.com/analytics.js';
r.parentNode.insertBefore(e,r)}(window,document,'script','ga'));
ga('create','UA-73375683-1','auto');ga('send','pageview');
</script>
</body>
</html>
================================================
FILE: web/js/app.js
================================================
window.mobilecheck = function() {
var check = false;
(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);
return check;
}
function rgb2hex(rgb){
rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
return (rgb && rgb.length === 4) ?
("0" + parseInt(rgb[1],10).toString(16)).slice(-2) +
("0" + parseInt(rgb[2],10).toString(16)).slice(-2) +
("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : "";
}
var make_z = function(max_length, scale) {
var z = []
var scale = scale | 2;
while(z.length < max_length){
var randomnumber=Math.random() * scale;
var found=false;
for(var i=0;i<z.length;i++){
if(z[i]==randomnumber){found=true;break}
}
if(!found)z[z.length]=randomnumber;
}
return z;
}
var get_pixels = function() {
var x = new Array(100);
var frame = PIXEL.getCurrentFrame();
for (var i=0; i < 10; i++) {
for (var j=0; j < 10; j++) {
var idx = i*10 + j;
if (frame[i][j] == "rgba(0, 0, 0, 0)") {
x[idx] = 0;
} else {
x[idx] = ((parseInt(frame[i][j].substring(1,3) ,16)/255*2)-1)/2;
}
}
}
return x;
}
var recover_pixels = function(x) {
for (var i=0; i < 10; i++) {
for (var j=0; j < 10; j++) {
var idx = i*10 + j;
x[idx] = ((x[idx] * 2) + 1) / 2 * 255;
}
}
return x;
}
var draw_pixels = function(x) {
var frame = PIXEL.getCurrentFrame();
for (var i=0; i < 10; i++) {
for (var j=0; j < 10; j++) {
var hex = Math.ceil(x[i*10+j]).toString(16);
PIXEL.setDraw(true);
PIXEL.doAction(i*20, j*20, "#" + hex + hex + hex);
PIXEL.setDraw(false);
}
}
}
var clip_pixel = function(x){
if(x>1) return 255;
else if(x<-1) return 0;
else return 255*(x+1.0)/2.0;
}
function cloneCanvas(oldCanvas) {
//create a new canvas
var newCanvas = document.createElement('canvas');
var context = newCanvas.getContext('2d');
//set dimensions
newCanvas.width = oldCanvas.width;
newCanvas.height = oldCanvas.height;
//apply the old canvas to the new one
context.drawImage(oldCanvas, 0, 0);
//return the new canvas
return newCanvas;
}
$( document ).ready(function() {
draw_pixels(make_z(100, 255));
$('.slick').slick({
slidesToShow: 2,
autoplay: true,
dots: true,
autoplaySpeed: 3000,
responsive: [
{
breakpoint: 980,
settings: {
slidesToShow: 1,
slidesToScroll: 1
}
}
]
});
$('.turing-slick').slick({
slidesToShow: 6,
autoplay: true,
dots: true,
autoplaySpeed: 3000,
responsive: [
{
breakpoint: 1200,
settings: {
slidesToShow: 5,
slidesToScroll: 5
}
},
{
breakpoint: 980,
settings: {
slidesToShow: 3,
slidesToScroll: 3
}
}
]
});
$("[data-toggle=tooltip]").tooltip();
var layer_defs = [];
layer_defs.push({type:"input", out_sx:1, out_sy:1, out_depth:100});
layer_defs.push({type:"deconv", sx:4, filters:512, stride:1, pad:0, bn:true, activation:"relu"});
layer_defs.push({type:"deconv", sx:4, filters:256, stride:2, pad:1, bn:true, activation:"relu"});
layer_defs.push({type:"deconv", sx:4, filters:128, stride:2, pad:1, bn:true, activation:"relu"});
layer_defs.push({type:"deconv", sx:4, filters:64, stride:2, pad:1, bn:true, activation:"relu"});
layer_defs.push({type:"deconv", sx:4, filters:3, stride:2, pad:1, activation:"tanh"});
var net = new convnetjs.Net();
net.makeLayers(layer_defs);
net.layers[1].fromJSON(layer_0);
net.layers[3].fromJSON(layer_1);
net.layers[5].fromJSON(layer_2);
net.layers[7].fromJSON(layer_3);
net.layers[9].fromJSON(layer_4);
var input = new convnetjs.Vol(1, 1, 100, 0.0);
var duplicates = [];
var pixels = [];
var draw = function() {
cur_pixel = get_pixels();
input.w = cur_pixel;
var output = net.forward(input);
var scale = 2;
var W = output.sx * scale;
var H = output.sy * scale;
var canv = document.createElement("canvas");
canv.width = W;
canv.height = H;
var ctx = canv.getContext("2d");
var g = ctx.createImageData(W, H);
for(var d=0; d < 3; d++) {
for(var x=0; x < output.sx; x++) {
for(var y=0; y < output.sy; y++) {
var dval = clip_pixel(output.get(x,y,d));
for(var dx = 0; dx < scale; dx++) {
for(var dy =0 ;dy < scale; dy++) {
var pp = ((W * (y*scale + dy)) + (dx + x*scale)) * 4;
g.data[pp + d] = dval;
if(d===0) g.data[pp+3] = 255; // alpha channel
}
}
}
}
}
ctx.putImageData(g, 0, 0);
document.getElementById("images").appendChild(canv);
duplicates.push(cloneCanvas(document.getElementById("pixel")));
pixels.push(cur_pixel);
$(canv).tooltip({
html: true,
template: '<div class="tooltip"><div class="tooltip-inner pixel-tooltip"></div></div>',
title: function(e) {
var duplicated = duplicates[parseInt($(this).attr("id")) - 1];
return duplicated;
},
}).hide()
.attr("id", duplicates.length)
.fadeIn(1000)
.click(function() {
draw_pixels(recover_pixels(pixels[parseInt($(this).attr("id")) - 1]));
});
}
$(".draw").click(draw);
$(".shuffle").click(function() {
draw_pixels(make_z(100, 255));
draw();
});
$("#loading").hide();
$("#draw-btn").show();
if (!mobilecheck()) {
draw();
}
$("#fakeLoader").fadeOut(3000);
});
// deactivate element
function deactivate($el) {
return $el.removeClass("active");
}
// activate element
function activate($el) {
return $el.addClass("active");
}
// disable element
function disable($el) {
return $el.addClass("disabled");
}
// enable element
function enable($el) {
return $el.removeClass("disabled");
}
// is element enabled?
function isEnabled($el) {
return $el.size() > 0 && !$el.hasClass("disabled");
}
// track event
function trackEvent(e, url) {
_gaq.push(['_trackEvent', 'Drawings', e, url]);
}
// returns mouse or tap event relative coordinates
function getCoordinates(e) {
var x, y;
x = e.offsetX ? e.offsetX : e.pageX - e.target.parentNode.offsetLeft;
y = e.offsetY ? e.offsetY : e.pageY - e.target.parentNode.offsetTop;
return {x: x, y: y};
}
var currentColor = "#000000",
copyFrameIndex = -1,
tips = true;
// mouse down event callback
function mouseDownCallback(e) {
PIXEL.setDraw(true);
var coordinates = getCoordinates(e);
PIXEL.doAction(coordinates.x, coordinates.y, currentColor);
}
// mouse move event callback
function mouseMoveCallback(e) {
var coordinates = getCoordinates(e);
PIXEL.doAction(coordinates.x, coordinates.y, currentColor);
e.preventDefault();
}
// mouse up event callback
function mouseUpCallback() {
PIXEL.setDraw(false);
}
var canvas = $("#pixel");
PIXEL.init(canvas[0], true);
// set drawing on mousedown
canvas.mousedown(mouseDownCallback).mousemove(mouseMoveCallback);
canvas.bind('touchstart', mouseDownCallback).bind('touchmove', mouseMoveCallback);
// reset drawing on mouseup
$(document).mouseup(mouseUpCallback);
$(document).bind('touchend', mouseUpCallback);
$(".action.selectable").click(function() {
PIXEL.setAction($(this).data('action'));
deactivate($(".action.selectable.active"));
activate($(this));
});
// colors
$(".color").click(function() {
currentColor = $(this).data('color');
deactivate($(".color.active"));
activate($(this));
});
// undo
$(".undo").click(function() {
PIXEL.undo();
});
// copy
$(".copy").click(function() {
copyFrameIndex = PIXEL.getCurrentFrameId();
});
$(".paste").click(function() {
if(copyFrameIndex > -1 && copyFrameIndex < PIXEL.getFramesLength()) {
PIXEL.pasteFrameAt(copyFrameIndex);
}
});
$(".rotate").click(function() {
PIXEL.rotate();
});
// jQuery to collapse the navbar on scroll
function collapseNavbar() {
if ($(".navbar").offset().top > 50) {
$(".navbar-fixed-top").addClass("top-nav-collapse");
} else {
$(".navbar-fixed-top").removeClass("top-nav-collapse");
}
}
$(window).scroll(collapseNavbar);
$(document).ready(collapseNavbar);
// jQuery for page scrolling feature - requires jQuery Easing plugin
$(function() {
$('a.page-scroll').bind('click', function(event) {
var $anchor = $(this);
$('html, body').stop().animate({
scrollTop: $($anchor.attr('href')).offset().top
}, 1500, 'easeInOutExpo');
event.preventDefault();
});
});
// Closes the Responsive Menu on Menu Item Click
$('.navbar-collapse ul li a').click(function() {
if ($(this).attr('class') != 'dropdown-toggle active' && $(this).attr('class') != 'dropdown-toggle') {
$('.navbar-toggle:visible').click();
}
});
================================================
FILE: web/js/convnet.js
================================================
var convnetjs = convnetjs || { REVISION: 'ALPHA' };
(function(global) {
"use strict";
// Random number utilities
var return_v = false;
var v_val = 0.0;
var gaussRandom = function() {
if(return_v) {
return_v = false;
return v_val;
}
var u = 2*Math.random()-1;
var v = 2*Math.random()-1;
var r = u*u + v*v;
if(r == 0 || r > 1) return gaussRandom();
var c = Math.sqrt(-2*Math.log(r)/r);
v_val = v*c; // cache this
return_v = true;
return u*c;
}
var randf = function(a, b) { return Math.random()*(b-a)+a; }
var randi = function(a, b) { return Math.floor(Math.random()*(b-a)+a); }
var randn = function(mu, std){ return mu+gaussRandom()*std; }
// Array utilities
var zeros = function(n) {
if(typeof(n)==='undefined' || isNaN(n)) { return []; }
if(typeof ArrayBuffer === 'undefined') {
// lacking browser support
var arr = new Array(n);
for(var i=0;i<n;i++) { arr[i]= 0; }
return arr;
} else {
return new Float64Array(n);
}
}
function average(data, nChannel){
var ans = zeros(nChannel);
for(var i=0; i < data.length; i++) {
ans[i % nChannel] += data[i];
}
var n = data.length / nChannel;
for(var i=0; i < nChannel; i++) {
ans[i] = ans[i] / n;
}
return ans;
}
function standardDeviation(v, nChannel){
var values = new Float64Array(v.length);
for(var i=0;i<v.length;i++) {
values[i] = v[i];
}
var avgs = average(values, nChannel);
for(var i=0; i < values.length; i++) {
var diff = (values[i] - avgs[i % nChannel]);
values[i] = diff * diff;
}
var stdDev = average(values, nChannel);
for(var i=0; i < nChannel; i++) {
stdDev[i] = Math.sqrt(stdDev[i] + 0.00001);
}
return stdDev;
}
var arrContains = function(arr, elt) {
for(var i=0,n=arr.length;i<n;i++) {
if(arr[i]===elt) return true;
}
return false;
}
var arrUnique = function(arr) {
var b = [];
for(var i=0,n=arr.length;i<n;i++) {
if(!arrContains(b, arr[i])) {
b.push(arr[i]);
}
}
return b;
}
// return max and min of a given non-empty array.
var maxmin = function(w) {
if(w.length === 0) { return {}; } // ... ;s
var maxv = w[0];
var minv = w[0];
var maxi = 0;
var mini = 0;
var n = w.length;
for(var i=1;i<n;i++) {
if(w[i] > maxv) { maxv = w[i]; maxi = i; }
if(w[i] < minv) { minv = w[i]; mini = i; }
}
return {maxi: maxi, maxv: maxv, mini: mini, minv: minv, dv:maxv-minv};
}
// create random permutation of numbers, in range [0...n-1]
var randperm = function(n) {
var i = n,
j = 0,
temp;
var array = [];
for(var q=0;q<n;q++)array[q]=q;
while (i--) {
j = Math.floor(Math.random() * (i+1));
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
return array;
}
// sample from list lst according to probabilities in list probs
// the two lists are of same size, and probs adds up to 1
var weightedSample = function(lst, probs) {
var p = randf(0, 1.0);
var cumprob = 0.0;
for(var k=0,n=lst.length;k<n;k++) {
cumprob += probs[k];
if(p < cumprob) { return lst[k]; }
}
}
// syntactic sugar function for getting default parameter values
var getopt = function(opt, field_name, default_value) {
if(typeof field_name === 'string') {
// case of single string
return (typeof opt[field_name] !== 'undefined') ? opt[field_name] : default_value;
} else {
// assume we are given a list of string instead
var ret = default_value;
for(var i=0;i<field_name.length;i++) {
var f = field_name[i];
if (typeof opt[f] !== 'undefined') {
ret = opt[f]; // overwrite return value
}
}
return ret;
}
}
function assert(condition, message) {
if (!condition) {
message = message || "Assertion failed";
if (typeof Error !== "undefined") {
throw new Error(message);
}
throw message; // Fallback
}
}
global.average = average;
global.standardDeviation = standardDeviation;
global.randi = randi;
global.randn = randn;
global.zeros = zeros;
global.maxmin = maxmin;
global.randperm = randperm;
global.weightedSample = weightedSample;
global.arrUnique = arrUnique;
global.arrContains = arrContains;
global.getopt = getopt;
global.assert = assert;
})(convnetjs);
(function(global) {
"use strict";
// Vol is the basic building block of all data in a net.
// it is essentially just a 3D volume of numbers, with a
// width (sx), height (sy), and depth (depth).
// it is used to hold data for all filters, all volumes,
// all weights, and also stores all gradients w.r.t.
// the data. c is optionally a value to initialize the volume
// with. If c is missing, fills the Vol with random numbers.
var Vol = function(sx, sy, depth, c) {
// this is how you check if a variable is an array. Oh, Javascript :)
if(Object.prototype.toString.call(sx) === '[object Array]') {
// we were given a list in sx, assume 1D volume and fill it up
this.sx = 1;
this.sy = 1;
this.depth = sx.length;
// we have to do the following copy because we want to use
// fast typed arrays, not an ordinary javascript array
this.w = global.zeros(this.depth);
this.dw = global.zeros(this.depth);
for(var i=0;i<this.depth;i++) {
this.w[i] = sx[i];
}
} else {
// we were given dimensions of the vol
this.sx = sx;
this.sy = sy;
this.depth = depth;
var n = sx*sy*depth;
this.w = global.zeros(n);
this.dw = global.zeros(n);
if(typeof c === 'undefined') {
// weight normalization is done to equalize the output
// variance of every neuron, otherwise neurons with a lot
// of incoming connections have outputs of larger variance
var scale = Math.sqrt(1.0/(sx*sy*depth));
for(var i=0;i<n;i++) {
this.w[i] = global.randn(0.0, scale);
}
} else {
for(var i=0;i<n;i++) {
this.w[i] = c;
}
}
}
}
Vol.prototype = {
get: function(x, y, d) {
var ix=((this.sx * y)+x)*this.depth+d;
return this.w[ix];
},
set: function(x, y, d, v) {
var ix=((this.sx * y)+x)*this.depth+d;
this.w[ix] = v;
},
add: function(x, y, d, v) {
var ix=((this.sx * y)+x)*this.depth+d;
this.w[ix] += v;
},
get_grad: function(x, y, d) {
var ix = ((this.sx * y)+x)*this.depth+d;
return this.dw[ix];
},
set_grad: function(x, y, d, v) {
var ix = ((this.sx * y)+x)*this.depth+d;
this.dw[ix] = v;
},
add_grad: function(x, y, d, v) {
var ix = ((this.sx * y)+x)*this.depth+d;
this.dw[ix] += v;
},
cloneAndZero: function() { return new Vol(this.sx, this.sy, this.depth, 0.0)},
clone: function() {
var V = new Vol(this.sx, this.sy, this.depth, 0.0);
var n = this.w.length;
for(var i=0;i<n;i++) { V.w[i] = this.w[i]; }
return V;
},
addFrom: function(V) { for(var k=0;k<this.w.length;k++) { this.w[k] += V.w[k]; }},
addFromScaled: function(V, a) { for(var k=0;k<this.w.length;k++) { this.w[k] += a*V.w[k]; }},
setConst: function(a) { for(var k=0;k<this.w.length;k++) { this.w[k] = a; }},
toJSON: function() {
// todo: we may want to only save d most significant digits to save space
var json = {}
json.sx = this.sx;
json.sy = this.sy;
json.depth = this.depth;
json.w = this.w;
return json;
// we wont back up gradients to save space
},
fromJSON: function(json) {
this.sx = json.sx;
this.sy = json.sy;
this.depth = json.depth;
var n = this.sx*this.sy*this.depth;
this.w = global.zeros(n);
this.dw = global.zeros(n);
// copy over the elements.
for(var i=0;i<n;i++) {
this.w[i] = json.w[i];
}
}
}
global.Vol = Vol;
})(convnetjs);
(function(global) {
"use strict";
var Vol = global.Vol; // convenience
// Volume utilities
// intended for use with data augmentation
// crop is the size of output
// dx,dy are offset wrt incoming volume, of the shift
// fliplr is boolean on whether we also want to flip left<->right
var augment = function(V, crop, dx, dy, fliplr) {
// note assumes square outputs of size crop x crop
if(typeof(fliplr)==='undefined') var fliplr = false;
if(typeof(dx)==='undefined') var dx = global.randi(0, V.sx - crop);
if(typeof(dy)==='undefined') var dy = global.randi(0, V.sy - crop);
// randomly sample a crop in the input volume
var W;
if(crop !== V.sx || dx!==0 || dy!==0) {
W = new Vol(crop, crop, V.depth, 0.0);
for(var x=0;x<crop;x++) {
for(var y=0;y<crop;y++) {
if(x+dx<0 || x+dx>=V.sx || y+dy<0 || y+dy>=V.sy) continue; // oob
for(var d=0;d<V.depth;d++) {
W.set(x,y,d,V.get(x+dx,y+dy,d)); // copy data over
}
}
}
} else {
W = V;
}
if(fliplr) {
// flip volume horziontally
var W2 = W.cloneAndZero();
for(var x=0;x<W.sx;x++) {
for(var y=0;y<W.sy;y++) {
for(var d=0;d<W.depth;d++) {
W2.set(x,y,d,W.get(W.sx - x - 1,y,d)); // copy data over
}
}
}
W = W2; //swap
}
return W;
}
// img is a DOM element that contains a loaded image
// returns a Vol of size (W, H, 4). 4 is for RGBA
var img_to_vol = function(img, convert_grayscale) {
if(typeof(convert_grayscale)==='undefined') var convert_grayscale = false;
var canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext("2d");
// due to a Firefox bug
try {
ctx.drawImage(img, 0, 0);
} catch (e) {
if (e.name === "NS_ERROR_NOT_AVAILABLE") {
// sometimes happens, lets just abort
return false;
} else {
throw e;
}
}
try {
var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
} catch (e) {
if(e.name === 'IndexSizeError') {
return false; // not sure what causes this sometimes but okay abort
} else {
throw e;
}
}
// prepare the input: get pixels and normalize them
var p = img_data.data;
var W = img.width;
var H = img.height;
var pv = []
for(var i=0;i<p.length;i++) {
pv.push(p[i]/255.0-0.5); // normalize image pixels to [-0.5, 0.5]
}
var x = new Vol(W, H, 4, 0.0); //input volume (image)
x.w = pv;
if(convert_grayscale) {
// flatten into depth=1 array
var x1 = new Vol(W, H, 1, 0.0);
for(var i=0;i<W;i++) {
for(var j=0;j<H;j++) {
x1.set(i,j,0,x.get(i,j,0));
}
}
x = x1;
}
return x;
}
global.augment = augment;
global.img_to_vol = img_to_vol;
})(convnetjs);
(function(global) {
"use strict";
var Vol = global.Vol; // convenience
// This file contains all layers that do dot products with input,
// but usually in a different connectivity pattern and weight sharing
// schemes:
// - FullyConn is fully connected dot products
// - ConvLayer does convolutions (so weight sharing spatially)
// - DeconvLayer does deconvolutions (so weight sharing spatially)
// putting them together in one file because they are very similar
var ConvLayer = function(opt) {
var opt = opt || {};
// required
this.out_depth = opt.filters;
this.sx = opt.sx; // filter size. Should be odd if possible, it's cleaner.
this.in_depth = opt.in_depth;
this.in_sx = opt.in_sx;
this.in_sy = opt.in_sy;
// optional
this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx;
this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 1; // stride at which we apply filters to input volume
this.pad = typeof opt.pad !== 'undefined' ? opt.pad : 0; // amount of 0 padding to add around borders of input volume
this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0;
this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0;
// computed
// note we are doing floor, so if the strided convolution of the filter doesnt fit into the input
// volume exactly, the output volume will be trimmed and not contain the (incomplete) computed
// final application.
this.out_sx = Math.floor((this.in_sx + this.pad * 2 - this.sx) / this.stride + 1);
this.out_sy = Math.floor((this.in_sy + this.pad * 2 - this.sy) / this.stride + 1);
this.layer_type = 'conv';
// initializations
var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0;
this.filters = [];
for(var i=0;i<this.out_depth;i++) { this.filters.push(new Vol(this.sx, this.sy, this.in_depth)); }
this.biases = new Vol(1, 1, this.out_depth, bias);
}
ConvLayer.prototype = {
forward: function(V, is_training) {
// optimized code by @mdda that achieves 2x speedup over previous version
this.in_act = V;
var A = new Vol(this.out_sx |0, this.out_sy |0, this.out_depth |0, 0.0);
var V_sx = V.sx |0;
var V_sy = V.sy |0;
var xy_stride = this.stride |0;
for(var d=0;d<this.out_depth;d++) {
var f = this.filters[d];
var x = -this.pad |0;
var y = -this.pad |0;
for(var ay=0; ay<this.out_sy; y+=xy_stride,ay++) { // xy_stride
x = -this.pad |0;
for(var ax=0; ax<this.out_sx; x+=xy_stride,ax++) { // xy_stride
// convolve centered at this particular location
var a = 0.0;
for(var fy=0;fy<f.sy;fy++) {
var oy = y+fy; // coordinates in the original input array coordinates
for(var fx=0;fx<f.sx;fx++) {
var ox = x+fx;
if(oy>=0 && oy<V_sy && ox>=0 && ox<V_sx) {
for(var fd=0;fd<f.depth;fd++) {
// avoid function call overhead (x2) for efficiency, compromise modularity :(
a += f.w[((f.sx * fy)+fx)*f.depth+fd] * V.w[((V_sx * oy)+ox)*V.depth+fd];
}
}
}
}
a += this.biases.w[d];
A.set(ax, ay, d, a);
}
}
}
this.out_act = A;
return this.out_act;
},
backward: function() {
var V = this.in_act;
V.dw = global.zeros(V.w.length); // zero out gradient wrt bottom data, we're about to fill it
var V_sx = V.sx |0;
var V_sy = V.sy |0;
var xy_stride = this.stride |0;
for(var d=0;d<this.out_depth;d++) {
var f = this.filters[d];
var x = -this.pad |0;
var y = -this.pad |0;
for(var ay=0; ay<this.out_sy; y+=xy_stride,ay++) { // xy_stride
x = -this.pad |0;
for(var ax=0; ax<this.out_sx; x+=xy_stride,ax++) { // xy_stride
// convolve centered at this particular location
var chain_grad = this.out_act.get_grad(ax,ay,d); // gradient from above, from chain rule
for(var fy=0;fy<f.sy;fy++) {
var oy = y+fy; // coordinates in the original input array coordinates
for(var fx=0;fx<f.sx;fx++) {
var ox = x+fx;
if(oy>=0 && oy<V_sy && ox>=0 && ox<V_sx) {
for(var fd=0;fd<f.depth;fd++) {
// avoid function call overhead (x2) for efficiency, compromise modularity :(
var ix1 = ((V_sx * oy)+ox)*V.depth+fd;
var ix2 = ((f.sx * fy)+fx)*f.depth+fd;
f.dw[ix2] += V.w[ix1]*chain_grad;
V.dw[ix1] += f.w[ix2]*chain_grad;
}
}
}
}
this.biases.dw[d] += chain_grad;
}
}
}
},
getParamsAndGrads: function() {
var response = [];
for(var i=0;i<this.out_depth;i++) {
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});
}
response.push({params: this.biases.w, grads: this.biases.dw, l1_decay_mul: 0.0, l2_decay_mul: 0.0});
return response;
},
toJSON: function() {
var json = {};
json.sx = this.sx; // filter size in x, y dims
json.sy = this.sy;
json.stride = this.stride;
json.in_depth = this.in_depth;
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.l1_decay_mul = this.l1_decay_mul;
json.l2_decay_mul = this.l2_decay_mul;
json.pad = this.pad;
json.filters = [];
for(var i=0;i<this.filters.length;i++) {
json.filters.push(this.filters[i].toJSON());
}
json.biases = this.biases.toJSON();
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.sx = json.sx; // filter size in x, y dims
this.sy = json.sy;
this.stride = json.stride;
this.in_depth = json.in_depth; // depth of input volume
this.filters = [];
this.l1_decay_mul = typeof json.l1_decay_mul !== 'undefined' ? json.l1_decay_mul : 1.0;
this.l2_decay_mul = typeof json.l2_decay_mul !== 'undefined' ? json.l2_decay_mul : 1.0;
this.pad = typeof json.pad !== 'undefined' ? json.pad : 0;
for(var i=0;i<json.filters.length;i++) {
var v = new Vol(0,0,0,0);
v.fromJSON(json.filters[i]);
this.filters.push(v);
}
this.biases = new Vol(0,0,0,0);
this.biases.fromJSON(json.biases);
}
}
var get_deconv_outsize = function(size, k, s, p) {
return s * (size - 1) + k - 2 * p;
}
var DeconvLayer = function(opt) {
var opt = opt || {};
// required
this.out_depth = opt.filters;
this.sx = opt.sx; // filter size. Should be odd if possible, it's cleaner.
this.in_depth = opt.in_depth;
this.in_sx = opt.in_sx;
this.in_sy = opt.in_sy;
this.bn = opt.bn;
// optional
this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx;
this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 1; // stride at which we apply filters to input volume
this.pad = typeof opt.pad !== 'undefined' ? opt.pad : 0; // amount of 0 padding to add around borders of input volume
this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0;
this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0;
// computed
// note we are doing floor, so if the strided convolution of the filter doesnt fit into the input
// volume exactly, the output volume will be trimmed and not contain the (incomplete) computed
// final application.
if(typeof opt.out_sx !== 'undefined') {
this.out_sx = opt.out_sx;
} else {
this.out_sx = get_deconv_outsize(this.in_sx, this.sx, this.stride, this.pad);
}
if(typeof opt.out_sy !== 'undefined') {
this.out_sy = opt.out_sy;
} else {
this.out_sy = get_deconv_outsize(this.in_sy, this.sy, this.stride, this.pad);
}
this.layer_type = 'deconv';
// initializations
var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0;
this.filters = [];
for(var i=0;i<this.out_depth;i++) { this.filters.push(new Vol(this.sx, this.sy, this.in_depth)); }
this.biases = new Vol(1, 1, this.out_depth, bias);
}
DeconvLayer.prototype = {
forward: function(V, is_training) {
// optimized code by @mdda that achieves 2x speedup over previous version
this.in_act = V;
var A = new Vol(this.out_sx |0, this.out_sy |0, this.out_depth |0, 0.0);
var V_sx = this.in_sx |0;
var V_sy = this.in_sy |0;
var V_depth = this.in_depth;
var xy_stride = this.stride |0;
for(var d=0;d<this.out_depth;d++) {
var f = this.filters[d];
for(var ay=0; ay<this.in_sy; y++,ay++) { // xy_stride
var y = (ay * xy_stride - this.pad)|0;
for(var ax=0; ax<this.in_sx; x+=xy_stride,ax++) { // xy_stride
var x = (ax * xy_stride - this.pad)|0;
// convolve centered at this particular location
for(var fy=0;fy<f.sy;fy++) {
var iy = y+fy; // coordinates in the original input array coordinates
for(var fx=0;fx<f.sx;fx++) {
var ix = x+fx;
if(iy>=0 && iy<A.sy && ix>=0 && ix<A.sx) {
for(var fd=0;fd<V_depth;fd++) {
var a = f.w[((f.sx * fy)+fx)*V_depth+fd] * V.w[((V_sx * ay)+ax)*V_depth+fd];
if (isNaN(a)) {
a = 1;
}
A.w[((A.sx * iy)+ix)*A.depth + d] += a;
}
}
}
}
}
}
for(var ay=0; ay<this.out_sy; y++,ay++) { // xy_stride
for(var ax=0; ax<this.out_sx; x++,ax++) { // xy_stride
A.w[((A.sx * ay)+ax)*A.depth + d] += this.biases.w[d];
}
}
}
if (this.bn) {
if (this.out_sy == 1) {
var avg = global.average(A.w, 512);
var std = global.standardDeviation(A.w, 512);
for(var i=0;i<this.out_depth;i++) {
A.w[i] = ((A.w[i] - avg[i % 512])/std[i % 512]) * this.gamma.w[i % 512] + this.beta.w[i % 512];
}
}
else {
var avg = global.average(A.w, this.out_depth);
var std = global.standardDeviation(A.w, this.out_depth);
for(var d=0;d<this.out_depth;d++) {
for(var ay=0; ay<this.out_sy; y++,ay++) { // xy_stride
for(var ax=0; ax<this.out_sx; x++,ax++) { // xy_stride
var i = ((A.sx * ay)+ax)*A.depth + d;
A.w[i] = ((A.w[i] - avg[d])/std[d])*this.gamma.w[d] + this.beta.w[d];
}
}
}
}
}
this.out_act = A;
return this.out_act;
},
backward: function() {
// Not implement.
},
getParamsAndGrads: function() {
var response = [];
for(var i=0;i<this.out_depth;i++) {
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});
}
response.push({params: this.biases.w, grads: this.biases.dw, l1_decay_mul: 0.0, l2_decay_mul: 0.0});
return response;
},
toJSON: function() {
var json = {};
json.sx = this.sx; // filter size in x, y dims
json.sy = this.sy;
json.stride = this.stride;
json.in_depth = this.in_depth;
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.l1_decay_mul = this.l1_decay_mul;
json.l2_decay_mul = this.l2_decay_mul;
json.pad = this.pad;
json.filters = [];
for(var i=0;i<this.filters.length;i++) {
json.filters.push(this.filters[i].toJSON());
}
json.biases = this.biases.toJSON();
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.sx = json.sx; // filter size in x, y dims
this.sy = json.sy;
this.stride = json.stride;
this.in_depth = json.in_depth; // depth of input volume
this.filters = [];
this.l1_decay_mul = typeof json.l1_decay_mul !== 'undefined' ? json.l1_decay_mul : 1.0;
this.l2_decay_mul = typeof json.l2_decay_mul !== 'undefined' ? json.l2_decay_mul : 1.0;
this.pad = typeof json.pad !== 'undefined' ? json.pad : 0;
for(var i=0;i<json.filters.length;i++) {
var v = new Vol(0,0,0,0);
v.fromJSON(json.filters[i]);
this.filters.push(v);
}
this.biases = new Vol(0,0,0,0);
this.biases.fromJSON(json.biases);
if (this.bn) {
this.gamma = new Vol(0,0,0,0);
this.gamma.fromJSON(json.gamma);
this.beta= new Vol(0,0,0,0);
this.beta.fromJSON(json.beta);
}
}
}
var FullyConnLayer = function(opt) {
var opt = opt || {};
// required
// ok fine we will allow 'filters' as the word as well
this.out_depth = typeof opt.num_neurons !== 'undefined' ? opt.num_neurons : opt.filters;
// optional
this.l1_decay_mul = typeof opt.l1_decay_mul !== 'undefined' ? opt.l1_decay_mul : 0.0;
this.l2_decay_mul = typeof opt.l2_decay_mul !== 'undefined' ? opt.l2_decay_mul : 1.0;
// computed
this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
this.out_sx = 1;
this.out_sy = 1;
this.layer_type = 'fc';
// initializations
var bias = typeof opt.bias_pref !== 'undefined' ? opt.bias_pref : 0.0;
this.filters = [];
for(var i=0;i<this.out_depth ;i++) { this.filters.push(new Vol(1, 1, this.num_inputs)); }
this.biases = new Vol(1, 1, this.out_depth, bias);
}
FullyConnLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var A = new Vol(1, 1, this.out_depth, 0.0);
var Vw = V.w;
for(var i=0;i<this.out_depth;i++) {
var a = 0.0;
var wi = this.filters[i].w;
for(var d=0;d<this.num_inputs;d++) {
a += Vw[d] * wi[d]; // for efficiency use Vols directly for now
}
a += this.biases.w[i];
A.w[i] = a;
}
this.out_act = A;
return this.out_act;
},
backward: function() {
var V = this.in_act;
V.dw = global.zeros(V.w.length); // zero out the gradient in input Vol
// compute gradient wrt weights and data
for(var i=0;i<this.out_depth;i++) {
var tfi = this.filters[i];
var chain_grad = this.out_act.dw[i];
for(var d=0;d<this.num_inputs;d++) {
V.dw[d] += tfi.w[d]*chain_grad; // grad wrt input data
tfi.dw[d] += V.w[d]*chain_grad; // grad wrt params
}
this.biases.dw[i] += chain_grad;
}
},
getParamsAndGrads: function() {
var response = [];
for(var i=0;i<this.out_depth;i++) {
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});
}
response.push({params: this.biases.w, grads: this.biases.dw, l1_decay_mul: 0.0, l2_decay_mul: 0.0});
return response;
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.num_inputs = this.num_inputs;
json.l1_decay_mul = this.l1_decay_mul;
json.l2_decay_mul = this.l2_decay_mul;
json.filters = [];
for(var i=0;i<this.filters.length;i++) {
json.filters.push(this.filters[i].toJSON());
}
json.biases = this.biases.toJSON();
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.num_inputs = json.num_inputs;
this.l1_decay_mul = typeof json.l1_decay_mul !== 'undefined' ? json.l1_decay_mul : 1.0;
this.l2_decay_mul = typeof json.l2_decay_mul !== 'undefined' ? json.l2_decay_mul : 1.0;
this.filters = [];
for(var i=0;i<json.filters.length;i++) {
var v = new Vol(0,0,0,0);
v.fromJSON(json.filters[i]);
this.filters.push(v);
}
this.biases = new Vol(0,0,0,0);
this.biases.fromJSON(json.biases);
}
}
global.ConvLayer = ConvLayer;
global.DeconvLayer = DeconvLayer;
global.FullyConnLayer = FullyConnLayer;
})(convnetjs);
(function(global) {
"use strict";
var Vol = global.Vol; // convenience
var PoolLayer = function(opt) {
var opt = opt || {};
// required
this.sx = opt.sx; // filter size
this.in_depth = opt.in_depth;
this.in_sx = opt.in_sx;
this.in_sy = opt.in_sy;
// optional
this.sy = typeof opt.sy !== 'undefined' ? opt.sy : this.sx;
this.stride = typeof opt.stride !== 'undefined' ? opt.stride : 2;
this.pad = typeof opt.pad !== 'undefined' ? opt.pad : 0; // amount of 0 padding to add around borders of input volume
// computed
this.out_depth = this.in_depth;
this.out_sx = Math.floor((this.in_sx + this.pad * 2 - this.sx) / this.stride + 1);
this.out_sy = Math.floor((this.in_sy + this.pad * 2 - this.sy) / this.stride + 1);
this.layer_type = 'pool';
// store switches for x,y coordinates for where the max comes from, for each output neuron
this.switchx = global.zeros(this.out_sx*this.out_sy*this.out_depth);
this.switchy = global.zeros(this.out_sx*this.out_sy*this.out_depth);
}
PoolLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var A = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0);
var n=0; // a counter for switches
for(var d=0;d<this.out_depth;d++) {
var x = -this.pad;
var y = -this.pad;
for(var ax=0; ax<this.out_sx; x+=this.stride,ax++) {
y = -this.pad;
for(var ay=0; ay<this.out_sy; y+=this.stride,ay++) {
// convolve centered at this particular location
var a = -99999; // hopefully small enough ;\
var winx=-1,winy=-1;
for(var fx=0;fx<this.sx;fx++) {
for(var fy=0;fy<this.sy;fy++) {
var oy = y+fy;
var ox = x+fx;
if(oy>=0 && oy<V.sy && ox>=0 && ox<V.sx) {
var v = V.get(ox, oy, d);
// perform max pooling and store pointers to where
// the max came from. This will speed up backprop
// and can help make nice visualizations in future
if(v > a) { a = v; winx=ox; winy=oy;}
}
}
}
this.switchx[n] = winx;
this.switchy[n] = winy;
n++;
A.set(ax, ay, d, a);
}
}
}
this.out_act = A;
return this.out_act;
},
backward: function() {
// pooling layers have no parameters, so simply compute
// gradient wrt data here
var V = this.in_act;
V.dw = global.zeros(V.w.length); // zero out gradient wrt data
var A = this.out_act; // computed in forward pass
var n = 0;
for(var d=0;d<this.out_depth;d++) {
var x = -this.pad;
var y = -this.pad;
for(var ax=0; ax<this.out_sx; x+=this.stride,ax++) {
y = -this.pad;
for(var ay=0; ay<this.out_sy; y+=this.stride,ay++) {
var chain_grad = this.out_act.get_grad(ax,ay,d);
V.add_grad(this.switchx[n], this.switchy[n], d, chain_grad);
n++;
}
}
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.sx = this.sx;
json.sy = this.sy;
json.stride = this.stride;
json.in_depth = this.in_depth;
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.pad = this.pad;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.sx = json.sx;
this.sy = json.sy;
this.stride = json.stride;
this.in_depth = json.in_depth;
this.pad = typeof json.pad !== 'undefined' ? json.pad : 0; // backwards compatibility
this.switchx = global.zeros(this.out_sx*this.out_sy*this.out_depth); // need to re-init these appropriately
this.switchy = global.zeros(this.out_sx*this.out_sy*this.out_depth);
}
}
global.PoolLayer = PoolLayer;
})(convnetjs);
(function(global) {
"use strict";
var Vol = global.Vol; // convenience
var getopt = global.getopt;
var InputLayer = function(opt) {
var opt = opt || {};
// required: depth
this.out_depth = getopt(opt, ['out_depth', 'depth'], 0);
// optional: default these dimensions to 1
this.out_sx = getopt(opt, ['out_sx', 'sx', 'width'], 1);
this.out_sy = getopt(opt, ['out_sy', 'sy', 'height'], 1);
// computed
this.layer_type = 'input';
}
InputLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
this.out_act = V;
return this.out_act; // simply identity function for now
},
backward: function() { },
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
}
}
global.InputLayer = InputLayer;
})(convnetjs);
(function(global) {
"use strict";
var Vol = global.Vol; // convenience
// Layers that implement a loss. Currently these are the layers that
// can initiate a backward() pass. In future we probably want a more
// flexible system that can accomodate multiple losses to do multi-task
// learning, and stuff like that. But for now, one of the layers in this
// file must be the final layer in a Net.
// This is a classifier, with N discrete classes from 0 to N-1
// it gets a stream of N incoming numbers and computes the softmax
// function (exponentiate and normalize to sum to 1 as probabilities should)
var SoftmaxLayer = function(opt) {
var opt = opt || {};
// computed
this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
this.out_depth = this.num_inputs;
this.out_sx = 1;
this.out_sy = 1;
this.layer_type = 'softmax';
}
SoftmaxLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var A = new Vol(1, 1, this.out_depth, 0.0);
// compute max activation
var as = V.w;
var amax = V.w[0];
for(var i=1;i<this.out_depth;i++) {
if(as[i] > amax) amax = as[i];
}
// compute exponentials (carefully to not blow up)
var es = global.zeros(this.out_depth);
var esum = 0.0;
for(var i=0;i<this.out_depth;i++) {
var e = Math.exp(as[i] - amax);
esum += e;
es[i] = e;
}
// normalize and output to sum to one
for(var i=0;i<this.out_depth;i++) {
es[i] /= esum;
A.w[i] = es[i];
}
this.es = es; // save these for backprop
this.out_act = A;
return this.out_act;
},
backward: function(y) {
// compute and accumulate gradient wrt weights and bias of this layer
var x = this.in_act;
x.dw = global.zeros(x.w.length); // zero out the gradient of input Vol
for(var i=0;i<this.out_depth;i++) {
var indicator = i === y ? 1.0 : 0.0;
var mul = -(indicator - this.es[i]);
x.dw[i] = mul;
}
// loss is the class negative log likelihood
return -Math.log(this.es[y]);
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.num_inputs = this.num_inputs;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.num_inputs = json.num_inputs;
}
}
// implements an L2 regression cost layer,
// so penalizes \sum_i(||x_i - y_i||^2), where x is its input
// and y is the user-provided array of "correct" values.
var RegressionLayer = function(opt) {
var opt = opt || {};
// computed
this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
this.out_depth = this.num_inputs;
this.out_sx = 1;
this.out_sy = 1;
this.layer_type = 'regression';
}
RegressionLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
this.out_act = V;
return V; // identity function
},
// y is a list here of size num_inputs
// or it can be a number if only one value is regressed
// or it can be a struct {dim: i, val: x} where we only want to
// regress on dimension i and asking it to have value x
backward: function(y) {
// compute and accumulate gradient wrt weights and bias of this layer
var x = this.in_act;
x.dw = global.zeros(x.w.length); // zero out the gradient of input Vol
var loss = 0.0;
if(y instanceof Array || y instanceof Float64Array) {
for(var i=0;i<this.out_depth;i++) {
var dy = x.w[i] - y[i];
x.dw[i] = dy;
loss += 0.5*dy*dy;
}
} else if(typeof y === 'number') {
// lets hope that only one number is being regressed
var dy = x.w[0] - y;
x.dw[0] = dy;
loss += 0.5*dy*dy;
} else {
// assume it is a struct with entries .dim and .val
// and we pass gradient only along dimension dim to be equal to val
var i = y.dim;
var yi = y.val;
var dy = x.w[i] - yi;
x.dw[i] = dy;
loss += 0.5*dy*dy;
}
return loss;
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.num_inputs = this.num_inputs;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.num_inputs = json.num_inputs;
}
}
var SVMLayer = function(opt) {
var opt = opt || {};
// computed
this.num_inputs = opt.in_sx * opt.in_sy * opt.in_depth;
this.out_depth = this.num_inputs;
this.out_sx = 1;
this.out_sy = 1;
this.layer_type = 'svm';
}
SVMLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
this.out_act = V; // nothing to do, output raw scores
return V;
},
backward: function(y) {
// compute and accumulate gradient wrt weights and bias of this layer
var x = this.in_act;
x.dw = global.zeros(x.w.length); // zero out the gradient of input Vol
// we're using structured loss here, which means that the score
// of the ground truth should be higher than the score of any other
// class, by a margin
var yscore = x.w[y]; // score of ground truth
var margin = 1.0;
var loss = 0.0;
for(var i=0;i<this.out_depth;i++) {
if(y === i) { continue; }
var ydiff = -yscore + x.w[i] + margin;
if(ydiff > 0) {
// violating dimension, apply loss
x.dw[i] += 1;
x.dw[y] -= 1;
loss += ydiff;
}
}
return loss;
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.num_inputs = this.num_inputs;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.num_inputs = json.num_inputs;
}
}
global.RegressionLayer = RegressionLayer;
global.SoftmaxLayer = SoftmaxLayer;
global.SVMLayer = SVMLayer;
})(convnetjs);
(function(global) {
"use strict";
var Vol = global.Vol; // convenience
// Implements ReLU nonlinearity elementwise
// x -> max(0, x)
// the output is in [0, inf)
var ReluLayer = function(opt) {
var opt = opt || {};
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'relu';
}
ReluLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var V2 = V.clone();
var N = V.w.length;
var V2w = V2.w;
for(var i=0;i<N;i++) {
if(V2w[i] < 0) V2w[i] = 0; // threshold at 0
}
this.out_act = V2;
return this.out_act;
},
backward: function() {
var V = this.in_act; // we need to set dw of this
var V2 = this.out_act;
var N = V.w.length;
V.dw = global.zeros(N); // zero out gradient wrt data
for(var i=0;i<N;i++) {
if(V2.w[i] <= 0) V.dw[i] = 0; // threshold
else V.dw[i] = V2.dw[i];
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
}
}
// Implements Sigmoid nnonlinearity elementwise
// x -> 1/(1+e^(-x))
// so the output is between 0 and 1.
var SigmoidLayer = function(opt) {
var opt = opt || {};
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'sigmoid';
}
SigmoidLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var V2 = V.cloneAndZero();
var N = V.w.length;
var V2w = V2.w;
var Vw = V.w;
for(var i=0;i<N;i++) {
V2w[i] = 1.0/(1.0+Math.exp(-Vw[i]));
}
this.out_act = V2;
return this.out_act;
},
backward: function() {
var V = this.in_act; // we need to set dw of this
var V2 = this.out_act;
var N = V.w.length;
V.dw = global.zeros(N); // zero out gradient wrt data
for(var i=0;i<N;i++) {
var v2wi = V2.w[i];
V.dw[i] = v2wi * (1.0 - v2wi) * V2.dw[i];
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
}
}
// Implements Maxout nnonlinearity that computes
// x -> max(x)
// where x is a vector of size group_size. Ideally of course,
// the input size should be exactly divisible by group_size
var MaxoutLayer = function(opt) {
var opt = opt || {};
// required
this.group_size = typeof opt.group_size !== 'undefined' ? opt.group_size : 2;
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = Math.floor(opt.in_depth / this.group_size);
this.layer_type = 'maxout';
this.switches = global.zeros(this.out_sx*this.out_sy*this.out_depth); // useful for backprop
}
MaxoutLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var N = this.out_depth;
var V2 = new Vol(this.out_sx, this.out_sy, this.out_depth, 0.0);
// optimization branch. If we're operating on 1D arrays we dont have
// to worry about keeping track of x,y,d coordinates inside
// input volumes. In convnets we do :(
if(this.out_sx === 1 && this.out_sy === 1) {
for(var i=0;i<N;i++) {
var ix = i * this.group_size; // base index offset
var a = V.w[ix];
var ai = 0;
for(var j=1;j<this.group_size;j++) {
var a2 = V.w[ix+j];
if(a2 > a) {
a = a2;
ai = j;
}
}
V2.w[i] = a;
this.switches[i] = ix + ai;
}
} else {
var n=0; // counter for switches
for(var x=0;x<V.sx;x++) {
for(var y=0;y<V.sy;y++) {
for(var i=0;i<N;i++) {
var ix = i * this.group_size;
var a = V.get(x, y, ix);
var ai = 0;
for(var j=1;j<this.group_size;j++) {
var a2 = V.get(x, y, ix+j);
if(a2 > a) {
a = a2;
ai = j;
}
}
V2.set(x,y,i,a);
this.switches[n] = ix + ai;
n++;
}
}
}
}
this.out_act = V2;
return this.out_act;
},
backward: function() {
var V = this.in_act; // we need to set dw of this
var V2 = this.out_act;
var N = this.out_depth;
V.dw = global.zeros(V.w.length); // zero out gradient wrt data
// pass the gradient through the appropriate switch
if(this.out_sx === 1 && this.out_sy === 1) {
for(var i=0;i<N;i++) {
var chain_grad = V2.dw[i];
V.dw[this.switches[i]] = chain_grad;
}
} else {
// bleh okay, lets do this the hard way
var n=0; // counter for switches
for(var x=0;x<V2.sx;x++) {
for(var y=0;y<V2.sy;y++) {
for(var i=0;i<N;i++) {
var chain_grad = V2.get_grad(x,y,i);
V.set_grad(x,y,this.switches[n],chain_grad);
n++;
}
}
}
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.group_size = this.group_size;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.group_size = json.group_size;
this.switches = global.zeros(this.group_size);
}
}
// a helper function, since tanh is not yet part of ECMAScript. Will be in v6.
function tanh(x) {
var y = Math.exp(2 * x);
return (y - 1) / (y + 1);
}
// Implements Tanh nnonlinearity elementwise
// x -> tanh(x)
// so the output is between -1 and 1.
var TanhLayer = function(opt) {
var opt = opt || {};
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'tanh';
}
TanhLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var V2 = V.cloneAndZero();
var N = V.w.length;
for(var i=0;i<N;i++) {
V2.w[i] = tanh(V.w[i]);
}
this.out_act = V2;
return this.out_act;
},
backward: function() {
var V = this.in_act; // we need to set dw of this
var V2 = this.out_act;
var N = V.w.length;
V.dw = global.zeros(N); // zero out gradient wrt data
for(var i=0;i<N;i++) {
var v2wi = V2.w[i];
V.dw[i] = (1.0 - v2wi * v2wi) * V2.dw[i];
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
}
}
global.TanhLayer = TanhLayer;
global.MaxoutLayer = MaxoutLayer;
global.ReluLayer = ReluLayer;
global.SigmoidLayer = SigmoidLayer;
})(convnetjs);
(function(global) {
"use strict";
var Vol = global.Vol; // convenience
// An inefficient dropout layer
// Note this is not most efficient implementation since the layer before
// computed all these activations and now we're just going to drop them :(
// same goes for backward pass. Also, if we wanted to be efficient at test time
// we could equivalently be clever and upscale during train and copy pointers during test
// todo: make more efficient.
var DropoutLayer = function(opt) {
var opt = opt || {};
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'dropout';
this.drop_prob = typeof opt.drop_prob !== 'undefined' ? opt.drop_prob : 0.5;
this.dropped = global.zeros(this.out_sx*this.out_sy*this.out_depth);
}
DropoutLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
if(typeof(is_training)==='undefined') { is_training = false; } // default is prediction mode
var V2 = V.clone();
var N = V.w.length;
if(is_training) {
// do dropout
for(var i=0;i<N;i++) {
if(Math.random()<this.drop_prob) { V2.w[i]=0; this.dropped[i] = true; } // drop!
else {this.dropped[i] = false;}
}
} else {
// scale the activations during prediction
for(var i=0;i<N;i++) { V2.w[i]*=this.drop_prob; }
}
this.out_act = V2;
return this.out_act; // dummy identity function for now
},
backward: function() {
var V = this.in_act; // we need to set dw of this
var chain_grad = this.out_act;
var N = V.w.length;
V.dw = global.zeros(N); // zero out gradient wrt data
for(var i=0;i<N;i++) {
if(!(this.dropped[i])) {
V.dw[i] = chain_grad.dw[i]; // copy over the gradient
}
}
},
getParamsAndGrads: function() {
return [];
},
toJSON: function() {
var json = {};
json.out_depth = this.out_depth;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.layer_type = this.layer_type;
json.drop_prob = this.drop_prob;
return json;
},
fromJSON: function(json) {
this.out_depth = json.out_depth;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.layer_type = json.layer_type;
this.drop_prob = json.drop_prob;
}
}
global.DropoutLayer = DropoutLayer;
})(convnetjs);
(function(global) {
"use strict";
var Vol = global.Vol; // convenience
// a bit experimental layer for now. I think it works but I'm not 100%
// the gradient check is a bit funky. I'll look into this a bit later.
// Local Response Normalization in window, along depths of volumes
var LocalResponseNormalizationLayer = function(opt) {
var opt = opt || {};
// required
this.k = opt.k;
this.n = opt.n;
this.alpha = opt.alpha;
this.beta = opt.beta;
// computed
this.out_sx = opt.in_sx;
this.out_sy = opt.in_sy;
this.out_depth = opt.in_depth;
this.layer_type = 'lrn';
// checks
if(this.n%2 === 0) { console.log('WARNING n should be odd for LRN layer'); }
}
LocalResponseNormalizationLayer.prototype = {
forward: function(V, is_training) {
this.in_act = V;
var A = V.cloneAndZero();
this.S_cache_ = V.cloneAndZero();
var n2 = Math.floor(this.n/2);
for(var x=0;x<V.sx;x++) {
for(var y=0;y<V.sy;y++) {
for(var i=0;i<V.depth;i++) {
var ai = V.get(x,y,i);
// normalize in a window of size n
var den = 0.0;
for(var j=Math.max(0,i-n2);j<=Math.min(i+n2,V.depth-1);j++) {
var aa = V.get(x,y,j);
den += aa*aa;
}
den *= this.alpha / this.n;
den += this.k;
this.S_cache_.set(x,y,i,den); // will be useful for backprop
den = Math.pow(den, this.beta);
A.set(x,y,i,ai/den);
}
}
}
this.out_act = A;
return this.out_act; // dummy identity function for now
},
backward: function() {
// evaluate gradient wrt data
var V = this.in_act; // we need to set dw of this
V.dw = global.zeros(V.w.length); // zero out gradient wrt data
var A = this.out_act; // computed in forward pass
var n2 = Math.floor(this.n/2);
for(var x=0;x<V.sx;x++) {
for(var y=0;y<V.sy;y++) {
for(var i=0;i<V.depth;i++) {
var chain_grad = this.out_act.get_grad(x,y,i);
var S = this.S_cache_.get(x,y,i);
var SB = Math.pow(S, this.beta);
var SB2 = SB*SB;
// normalize in a window of size n
for(var j=Math.max(0,i-n2);j<=Math.min(i+n2,V.depth-1);j++) {
var aj = V.get(x,y,j);
var g = -aj*this.beta*Math.pow(S,this.beta-1)*this.alpha/this.n*2*aj;
if(j===i) g+= SB;
g /= SB2;
g *= chain_grad;
V.add_grad(x,y,j,g);
}
}
}
}
},
getParamsAndGrads: function() { return []; },
toJSON: function() {
var json = {};
json.k = this.k;
json.n = this.n;
json.alpha = this.alpha; // normalize by size
json.beta = this.beta;
json.out_sx = this.out_sx;
json.out_sy = this.out_sy;
json.out_depth = this.out_depth;
json.layer_type = this.layer_type;
return json;
},
fromJSON: function(json) {
this.k = json.k;
this.n = json.n;
this.alpha = json.alpha; // normalize by size
this.beta = json.beta;
this.out_sx = json.out_sx;
this.out_sy = json.out_sy;
this.out_depth = json.out_depth;
this.layer_type = json.layer_type;
}
}
global.LocalResponseNormalizationLayer = LocalResponseNormalizationLayer;
})(convnetjs);
(function(global) {
"use strict";
var Vol = global.Vol; // convenience
var assert = global.assert;
// Net manages a set of layers
// For now constraints: Simple linear order of layers, first layer input last layer a cost layer
var Net = function(options) {
this.layers = [];
}
Net.prototype = {
// takes a list of layer definitions and creates the network layer objects
makeLayers: function(defs) {
// few checks
assert(defs.length >= 2, 'Error! At least one input layer and one loss layer are required.');
assert(defs[0].type === 'input', 'Error! First layer must be the input layer, to declare size of inputs');
// desugar layer_defs for adding activation, dropout layers etc
var desugar = function() {
var new_defs = [];
for(var i=0;i<defs.length;i++) {
var def = defs[i];
if(def.type==='softmax' || def.type==='svm') {
// add an fc layer here, there is no reason the user should
// have to worry about this and we almost always want to
new_defs.push({type:'fc', num_neurons: def.num_classes});
}
if(def.type==='regression') {
// add an fc layer here, there is no reason the user should
// have to worry about this and we almost always want to
new_defs.push({type:'fc', num_neurons: def.num_neurons});
}
if((def.type==='fc' || def.type==='conv')
&& typeof(def.bias_pref) === 'undefined'){
def.bias_pref = 0.0;
if(typeof def.activation !== 'undefined' && def.activation === 'relu') {
def.bias_pref = 0.1; // relus like a bit of positive bias to get gradients early
// otherwise it's technically possible that a relu unit will never turn on (by chance)
// and will never get any gradient and never contribute any computation. Dead relu.
}
}
new_defs.push(def);
if(typeof def.activation !== 'undefined') {
if(def.activation==='relu') { new_defs.push({type:'relu'}); }
else if (def.activation==='sigmoid') { new_defs.push({type:'sigmoid'}); }
else if (def.activation==='tanh') { new_defs.push({type:'tanh'}); }
else if (def.activation==='maxout') {
// create maxout activation, and pass along group size, if provided
var gs = def.group_size !== 'undefined' ? def.group_size : 2;
new_defs.push({type:'maxout', group_size:gs});
}
else { console.log('ERROR unsupported activation ' + def.activation); }
}
if(typeof def.drop_prob !== 'undefined' && def.type !== 'dropout') {
new_defs.push({type:'dropout', drop_prob: def.drop_prob});
}
}
return new_defs;
}
defs = desugar(defs);
// create the layers
this.layers = [];
for(var i=0;i<defs.length;i++) {
var def = defs[i];
if(i>0) {
var prev = this.layers[i-1];
def.in_sx = prev.out_sx;
def.in_sy = prev.out_sy;
def.in_depth = prev.out_depth;
}
switch(def.type) {
case 'fc': this.layers.push(new global.FullyConnLayer(def)); break;
case 'lrn': this.layers.push(new global.LocalResponseNormalizationLayer(def)); break;
case 'dropout': this.layers.push(new global.DropoutLayer(def)); break;
case 'input': this.layers.push(new global.InputLayer(def)); break;
case 'softmax': this.layers.push(new global.SoftmaxLayer(def)); break;
case 'regression': this.layers.push(new global.RegressionLayer(def)); break;
case 'conv': this.layers.push(new global.ConvLayer(def)); break;
case 'deconv': this.layers.push(new global.DeconvLayer(def)); break;
case 'pool': this.layers.push(new global.PoolLayer(def)); break;
case 'relu': this.layers.push(new global.ReluLayer(def)); break;
case 'sigmoid': this.layers.push(new global.SigmoidLayer(def)); break;
case 'tanh': this.layers.push(new global.TanhLayer(def)); break;
case 'maxout': this.layers.push(new global.MaxoutLayer(def)); break;
case 'svm': this.layers.push(new global.SVMLayer(def)); break;
default: console.log('ERROR: UNRECOGNIZED LAYER TYPE: ' + def.type);
}
}
},
// forward prop the network.
// The trainer class passes is_training = true, but when this function is
// called from outside (not from the trainer), it defaults to prediction mode
forward: function(V, is_training) {
if(typeof(is_training) === 'undefined') is_training = false;
var act = this.layers[0].forward(V, is_training);
for(var i=1;i<this.layers.length;i++) {
act = this.layers[i].forward(act, is_training);
}
return act;
},
getCostLoss: function(V, y) {
this.forward(V, false);
var N = this.layers.length;
var loss = this.layers[N-1].backward(y);
return loss;
},
// backprop: compute gradients wrt all parameters
backward: function(y) {
var N = this.layers.length;
var loss = this.layers[N-1].backward(y); // last layer assumed to be loss layer
for(var i=N-2;i>=0;i--) { // first layer assumed input
this.layers[i].backward();
}
return loss;
},
getParamsAndGrads: function() {
// accumulate parameters and gradients for the entire network
var response = [];
for(var i=0;i<this.layers.length;i++) {
var layer_reponse = this.layers[i].getParamsAndGrads();
for(var j=0;j<layer_reponse.length;j++) {
response.push(layer_reponse[j]);
}
}
return response;
},
getPrediction: function() {
// this is a convenience function for returning the argmax
// prediction, assuming the last layer of the net is a softmax
var S = this.layers[this.layers.length-1];
assert(S.layer_type === 'softmax', 'getPrediction function assumes softmax as last layer of the net!');
var p = S.out_act.w;
var maxv = p[0];
var maxi = 0;
for(var i=1;i<p.length;i++) {
if(p[i] > maxv) { maxv = p[i]; maxi = i;}
}
return maxi; // return index of the class with highest class probability
},
toJSON: function() {
var json = {};
json.layers = [];
for(var i=0;i<this.layers.length;i++) {
json.layers.push(this.layers[i].toJSON());
}
return json;
},
fromJSON: function(json) {
this.layers = [];
for(var i=0;i<json.layers.length;i++) {
var Lj = json.layers[i]
var t = Lj.layer_type;
var L;
if(t==='input') { L = new global.InputLayer(); }
if(t==='relu') { L = new global.ReluLayer(); }
if(t==='sigmoid') { L = new global.SigmoidLayer(); }
if(t==='tanh') { L = new global.TanhLayer(); }
if(t==='dropout') { L = new global.DropoutLayer(); }
if(t==='conv') { L = new global.ConvLayer(); }
if(t==='pool') { L = new global.PoolLayer(); }
if(t==='lrn') { L = new global.LocalResponseNormalizationLayer(); }
if(t==='softmax') { L = new global.SoftmaxLayer(); }
if(t==='regression') { L = new global.RegressionLayer(); }
if(t==='fc') { L = new global.FullyConnLayer(); }
if(t==='maxout') { L = new global.MaxoutLayer(); }
if(t==='svm') { L = new global.SVMLayer(); }
L.fromJSON(Lj);
this.layers.push(L);
}
}
}
global.Net = Net;
})(convnetjs);
(function(global) {
"use strict";
var Vol = global.Vol; // convenience
var Trainer = function(net, options) {
this.net = net;
var options = options || {};
this.learning_rate = typeof options.learning_rate !== 'undefined' ? options.learning_rate : 0.01;
this.l1_decay = typeof options.l1_decay !== 'undefined' ? options.l1_decay : 0.0;
this.l2_decay = typeof options.l2_decay !== 'undefined' ? options.l2_decay : 0.0;
this.batch_size = typeof options.batch_size !== 'undefined' ? options.batch_size : 1;
this.method = typeof options.method !== 'undefined' ? options.method : 'sgd'; // sgd/adam/adagrad/adadelta/windowgrad/netsterov
this.momentum = typeof options.momentum !== 'undefined' ? options.momentum : 0.9;
this.ro = typeof options.ro !== 'undefined' ? options.ro : 0.95; // used in adadelta
this.eps = typeof options.eps !== 'undefined' ? options.eps : 1e-8; // used in adam or adadelta
this.beta1 = typeof options.beta1 !== 'undefined' ? options.beta1 : 0.9; // used in adam
this.beta2 = typeof options.beta2 !== 'undefined' ? options.beta2 : 0.999; // used in adam
this.k = 0; // iteration counter
this.gsum = []; // last iteration gradients (used for momentum calculations)
this.xsum = []; // used in adam or adadelta
// check if regression is expected
if(this.net.layers[this.net.layers.length - 1].layer_type === "regression")
this.regression = true;
else
this.regression = false;
}
Trainer.prototype = {
train: function(x, y) {
var start = new Date().getTime();
this.net.forward(x, true); // also set the flag that lets the net know we're just training
var end = new Date().getTime();
var fwd_time = end - start;
var start = new Date().getTime();
var cost_loss = this.net.backward(y);
var l2_decay_loss = 0.0;
var l1_decay_loss = 0.0;
var end = new Date().getTime();
var bwd_time = end - start;
if(this.regression && y.constructor !== Array)
console.log("Warning: a regression net requires an array as training output vector.");
this.k++;
if(this.k % this.batch_size === 0) {
var pglist = this.net.getParamsAndGrads();
// initialize lists for accumulators. Will only be done once on first iteration
if(this.gsum.length === 0 && (this.method !== 'sgd' || this.momentum > 0.0)) {
// only vanilla sgd doesnt need either lists
// momentum needs gsum
// adagrad needs gsum
// adam and adadelta needs gsum and xsum
for(var i=0;i<pglist.length;i++) {
this.gsum.push(global.zeros(pglist[i].params.length));
if(this.method === 'adam' || this.method === 'adadelta') {
this.xsum.push(global.zeros(pglist[i].params.length));
} else {
this.xsum.push([]); // conserve memory
}
}
}
// perform an update for all sets of weights
for(var i=0;i<pglist.length;i++) {
var pg = pglist[i]; // param, gradient, other options in future (custom learning rate etc)
var p = pg.params;
var g = pg.grads;
// learning rate for some parameters.
var l2_decay_mul = typeof pg.l2_decay_mul !== 'undefined' ? pg.l2_decay_mul : 1.0;
var l1_decay_mul = typeof pg.l1_decay_mul !== 'undefined' ? pg.l1_decay_mul : 1.0;
var l2_decay = this.l2_decay * l2_decay_mul;
var l1_decay = this.l1_decay * l1_decay_mul;
var plen = p.length;
for(var j=0;j<plen;j++) {
l2_decay_loss += l2_decay*p[j]*p[j]/2; // accumulate weight decay loss
l1_decay_loss += l1_decay*Math.abs(p[j]);
var l1grad = l1_decay * (p[j] > 0 ? 1 : -1);
var l2grad = l2_decay * (p[j]);
var gij = (l2grad + l1grad + g[j]) / this.batch_size; // raw batch gradient
var gsumi = this.gsum[i];
var xsumi = this.xsum[i];
if(this.method === 'adam') {
// adam update
gsumi[j] = gsumi[j] * this.beta1 + (1- this.beta1) * gij; // update biased first moment estimate
xsumi[j] = xsumi[j] * this.beta2 + (1-this.beta2) * gij * gij; // update biased second moment estimate
var biasCorr1 = gsumi[j] * (1 - Math.pow(this.beta1, this.k)); // correct bias first moment estimate
var biasCorr2 = xsumi[j] * (1 - Math.pow(this.beta2, this.k)); // correct bias second moment estimate
var dx = - this.learning_rate * biasCorr1 / (Math.sqrt(biasCorr2) + this.eps);
p[j] += dx;
} else if(this.method === 'adagrad') {
// adagrad update
gsumi[j] = gsumi[j] + gij * gij;
var dx = - this.learning_rate / Math.sqrt(gsumi[j] + this.eps) * gij;
p[j] += dx;
} else if(this.method === 'windowgrad') {
// this is adagrad but with a moving window weighted average
// so the gradient is not accumulated over the entire history of the run.
// it's also referred to as Idea #1 in Zeiler paper on Adadelta. Seems reasonable to me!
gsumi[j] = this.ro * gsumi[j] + (1-this.ro) * gij * gij;
var dx = - this.learning_rate / Math.sqrt(gsumi[j] + this.eps) * gij; // eps added for better conditioning
p[j] += dx;
} else if(this.method === 'adadelta') {
gsumi[j] = this.ro * gsumi[j] + (1-this.ro) * gij * gij;
var dx = - Math.sqrt((xsumi[j] + this.eps)/(gsumi[j] + this.eps)) * gij;
xsumi[j] = this.ro * xsumi[j] + (1-this.ro) * dx * dx; // yes, xsum lags behind gsum by 1.
p[j] += dx;
} else if(this.method === 'nesterov') {
var dx = gsumi[j];
gsumi[j] = gsumi[j] * this.momentum + this.learning_rate * gij;
dx = this.momentum * dx - (1.0 + this.momentum) * gsumi[j];
p[j] += dx;
} else {
// assume SGD
if(this.momentum > 0.0) {
// momentum update
var dx = this.momentum * gsumi[j] - this.learning_rate * gij; // step
gsumi[j] = dx; // back this up for next iteration of momentum
p[j] += dx; // apply corrected gradient
} else {
// vanilla sgd
p[j] += - this.learning_rate * gij;
}
}
g[j] = 0.0; // zero out gradient so that we can begin accumulating anew
}
}
}
// appending softmax_loss for backwards compatibility, but from now on we will always use cost_loss
// in future, TODO: have to completely redo the way loss is done around the network as currently
// loss is a bit of a hack. Ideally, user should specify arbitrary number of loss functions on any layer
// and it should all be computed correctly and automatically.
return {fwd_time: fwd_time, bwd_time: bwd_time,
l2_decay_loss: l2_decay_loss, l1_decay_loss: l1_decay_loss,
cost_loss: cost_loss, softmax_loss: cost_loss,
loss: cost_loss + l1_decay_loss + l2_decay_loss}
}
}
global.Trainer = Trainer;
global.SGDTrainer = Trainer; // backwards compatibility
})(convnetjs);
(function(global) {
"use strict";
// used utilities, make explicit local references
var randf = global.randf;
var randi = global.randi;
var Net = global.Net;
var Trainer = global.Trainer;
var maxmin = global.maxmin;
var randperm = global.randperm;
var weightedSample = global.weightedSample;
var getopt = global.getopt;
var arrUnique = global.arrUnique;
/*
A MagicNet takes data: a list of convnetjs.Vol(), and labels
which for now are assumed to be class indeces 0..K. MagicNet then:
- creates data folds for cross-validation
- samples candidate networks
- evaluates candidate networks on all data folds
- produces predictions by model-averaging the best networks
*/
var MagicNet = function(data, labels, opt) {
var opt = opt || {};
if(typeof data === 'undefined') { data = []; }
if(typeof labels === 'undefined') { labels = []; }
// required inputs
this.data = data; // store these pointers to data
this.labels = labels;
// optional inputs
this.train_ratio = getopt(opt, 'train_ratio', 0.7);
this.num_folds = getopt(opt, 'num_folds', 10);
this.num_candidates = getopt(opt, 'num_candidates', 50); // we evaluate several in parallel
// how many epochs of data to train every network? for every fold?
// higher values mean higher accuracy in final results, but more expensive
this.num_epochs = getopt(opt, 'num_epochs', 50);
// number of best models to average during prediction. Usually higher = better
this.ensemble_size = getopt(opt, 'ensemble_size', 10);
// candidate parameters
this.batch_size_min = getopt(opt, 'batch_size_min', 10);
this.batch_size_max = getopt(opt, 'batch_size_max', 300);
this.l2_decay_min = getopt(opt, 'l2_decay_min', -4);
this.l2_decay_max = getopt(opt, 'l2_decay_max', 2);
this.learning_rate_min = getopt(opt, 'learning_rate_min', -4);
this.learning_rate_max = getopt(opt, 'learning_rate_max', 0);
this.momentum_min = getopt(opt, 'momentum_min', 0.9);
this.momentum_max = getopt(opt, 'momentum_max', 0.9);
this.neurons_min = getopt(opt, 'neurons_min', 5);
this.neurons_max = getopt(opt, 'neurons_max', 30);
// computed
this.folds = []; // data fold indices, gets filled by sampleFolds()
this.candidates = []; // candidate networks that are being currently evaluated
this.evaluated_candidates = []; // history of all candidates that were fully evaluated on all folds
this.unique_labels = arrUnique(labels);
this.iter = 0; // iteration counter, goes from 0 -> num_epochs * num_training_data
this.foldix = 0; // index of active fold
// callbacks
this.finish_fold_callback = null;
this.finish_batch_callback = null;
// initializations
if(this.data.length > 0) {
this.sampleFolds();
this.sampleCandidates();
}
};
MagicNet.prototype = {
// sets this.folds to a sampling of this.num_folds folds
sampleFolds: function() {
var N = this.data.length;
var num_train = Math.floor(this.train_ratio * N);
this.folds = []; // flush folds, if any
for(var i=0;i<this.num_folds;i++) {
var p = randperm(N);
this.folds.push({train_ix: p.slice(0, num_train), test_ix: p.slice(num_train, N)});
}
},
// returns a random candidate network
sampleCandidate: function() {
var input_depth = this.data[0].w.length;
var num_classes = this.unique_labels.length;
// sample network topology and hyperparameters
var layer_defs = [];
layer_defs.push({type:'input', out_sx:1, out_sy:1, out_depth: input_depth});
var nl = weightedSample([0,1,2,3], [0.2, 0.3, 0.3, 0.2]); // prefer nets with 1,2 hidden layers
for(var q=0;q<nl;q++) {
var ni = randi(this.neurons_min, this.neurons_max);
var act = ['tanh','maxout','relu'][randi(0,3)];
if(randf(0,1)<0.5) {
var dp = Math.random();
layer_defs.push({type:'fc', num_neurons: ni, activation: act, drop_prob: dp});
} else {
layer_defs.push({type:'fc', num_neurons: ni, activation: act});
}
}
layer_defs.push({type:'softmax', num_classes: num_classes});
var net = new Net();
net.makeLayers(layer_defs);
// sample training hyperparameters
var bs = randi(this.batch_size_min, this.batch_size_max); // batch size
var l2 = Math.pow(10, randf(this.l2_decay_min, this.l2_decay_max)); // l2 weight decay
var lr = Math.pow(10, randf(this.learning_rate_min, this.learning_rate_max)); // learning rate
var mom = randf(this.momentum_min, this.momentum_max); // momentum. Lets just use 0.9, works okay usually ;p
var tp = randf(0,1); // trainer type
var trainer_def;
if(tp<0.33) {
trainer_def = {method:'adadelta', batch_size:bs, l2_decay:l2};
} else if(tp<0.66) {
trainer_def = {method:'adagrad', learning_rate: lr, batch_size:bs, l2_decay:l2};
} else {
trainer_def = {method:'sgd', learning_rate: lr, momentum: mom, batch_size:bs, l2_decay:l2};
}
var trainer = new Trainer(net, trainer_def);
var cand = {};
cand.acc = [];
cand.accv = 0; // this will maintained as sum(acc) for convenience
cand.layer_defs = layer_defs;
cand.trainer_def = trainer_def;
cand.net = net;
cand.trainer = trainer;
return cand;
},
// sets this.candidates with this.num_candidates candidate nets
sampleCandidates: function() {
this.candidates = []; // flush, if any
for(var i=0;i<this.num_candidates;i++) {
var cand = this.sampleCandidate();
this.candidates.push(cand);
}
},
step: function() {
// run an example through current candidate
this.iter++;
// step all candidates on a random data point
var fold = this.folds[this.foldix]; // active fold
var dataix = fold.train_ix[randi(0, fold.train_ix.length)];
for(var k=0;k<this.candidates.length;k++) {
var x = this.data[dataix];
var l = this.labels[dataix];
this.candidates[k].trainer.train(x, l);
}
// process consequences: sample new folds, or candidates
var lastiter = this.num_epochs * fold.train_ix.length;
if(this.iter >= lastiter) {
// finished evaluation of this fold. Get final validation
// accuracies, record them, and go on to next fold.
var val_acc = this.evalValErrors();
for(var k=0;k<this.candidates.length;k++) {
var c = this.candidates[k];
c.acc.push(val_acc[k]);
c.accv += val_acc[k];
}
this.iter = 0; // reset step number
this.foldix++; // increment fold
if(this.finish_fold_callback !== null) {
this.finish_fold_callback();
}
if(this.foldix >= this.folds.length) {
// we finished all folds as well! Record these candidates
// and sample new ones to evaluate.
for(var k=0;k<this.candidates.length;k++) {
this.evaluated_candidates.push(this.candidates[k]);
}
// sort evaluated candidates according to accuracy achieved
this.evaluated_candidates.sort(function(a, b) {
return (a.accv / a.acc.length)
> (b.accv / b.acc.length)
? -1 : 1;
});
// and clip only to the top few ones (lets place limit at 3*ensemble_size)
// otherwise there are concerns with keeping these all in memory
// if MagicNet is being evaluated for a very long time
if(this.evaluated_candidates.length > 3 * this.ensemble_size) {
this.evaluated_candidates = this.evaluated_candidates.slice(0, 3 * this.ensemble_size);
}
if(this.finish_batch_callback !== null) {
this.finish_batch_callback();
}
this.sampleCandidates(); // begin with new candidates
this.foldix = 0; // reset this
} else {
// we will go on to another fold. reset all candidates nets
for(var k=0;k<this.candidates.length;k++) {
var c = this.candidates[k];
var net = new Net();
net.makeLayers(c.layer_defs);
var trainer = new Trainer(net, c.trainer_def);
c.net = net;
c.trainer = trainer;
}
}
}
},
evalValErrors: function() {
// evaluate candidates on validation data and return performance of current networks
// as simple list
var vals = [];
var fold = this.folds[this.foldix]; // active fold
for(var k=0;k<this.candidates.length;k++) {
var net = this.candidates[k].net;
var v = 0.0;
for(var q=0;q<fold.test_ix.length;q++) {
var x = this.data[fold.test_ix[q]];
var l = this.labels[fold.test_ix[q]];
net.forward(x);
var yhat = net.getPrediction();
v += (yhat === l ? 1.0 : 0.0); // 0 1 loss
}
v /= fold.test_ix.length; // normalize
vals.push(v);
}
return vals;
},
// returns prediction scores for given test data point, as Vol
// uses an averaged prediction from the best ensemble_size models
// x is a Vol.
predict_soft: function(data) {
// forward prop the best networks
// and accumulate probabilities at last layer into a an output Vol
var eval_candidates = [];
var nv = 0;
if(this.evaluated_candidates.length === 0) {
// not sure what to do here, first batch of nets hasnt evaluated yet
// lets just predict with current candidates.
nv = this.candidates.length;
eval_candidates = this.candidates;
} else {
// forward prop the best networks from evaluated_candidates
nv = Math.min(this.ensemble_size, this.evaluated_candidates.length);
eval_candidates = this.evaluated_candidates
}
// forward nets of all candidates and average the predictions
var xout, n;
for(var j=0;j<nv;j++) {
var net = eval_candidates[j].net;
var x = net.forward(data);
if(j===0) {
xout = x;
n = x.w.length;
} else {
// add it on
for(var d=0;d<n;d++) {
xout.w[d] += x.w[d];
}
}
}
// produce average
for(var d=0;d<n;d++) {
xout.w[d] /= nv;
}
return xout;
},
predict: function(data) {
var xout = this.predict_soft(data);
if(xout.w.length !== 0) {
var stats = maxmin(xout.w);
var predicted_label = stats.maxi;
} else {
var predicted_label = -1; // error out
}
return predicted_label;
},
toJSON: function() {
// dump the top ensemble_size networks as a list
var nv = Math.min(this.ensemble_size, this.evaluated_candidates.length);
var json = {};
json.nets = [];
for(var i=0;i<nv;i++) {
json.nets.push(this.evaluated_candidates[i].net.toJSON());
}
return json;
},
fromJSON: function(json) {
this.ensemble_size = json.nets.length;
this.evaluated_candidates = [];
for(var i=0;i<this.ensemble_size;i++) {
var net = new Net();
net.fromJSON(json.nets[i]);
var dummy_candidate = {};
dummy_candidate.net = net;
this.evaluated_candidates.push(dummy_candidate);
}
},
// callback functions
// called when a fold is finished, while evaluating a batch
onFinishFold: function(f) { this.finish_fold_callback = f; },
// called when a batch of candidates has finished evaluating
onFinishBatch: function(f) { this.finish_batch_callback = f; }
};
global.MagicNet = MagicNet;
})(convnetjs);
(function(lib) {
"use strict";
if (typeof module === "undefined" || typeof module.exports === "undefined") {
window.convnetjs = lib; // in ordinary browser attach library to window
} else {
module.exports = lib; // in nodejs
}
})(convnetjs);
================================================
FILE: web/js/layers.js
================================================
[File too large to display: 32.2 MB]
gitextract_xriuzdbv/
├── .gitignore
├── LICENSE
├── README.md
├── download.py
├── main.py
├── model.py
├── ops.py
├── utils.py
└── web/
├── app.py
├── css/
│ ├── fakeLoader.css
│ └── main.css
├── fonts/
│ └── FontAwesome.otf
├── index.html
└── js/
├── app.js
├── convnet.js
└── layers.js
SYMBOL INDEX (69 symbols across 8 files)
FILE: download.py
function download (line 27) | def download(url, dirpath):
function download_file_from_google_drive (line 54) | def download_file_from_google_drive(id, destination):
function get_confirm_token (line 67) | def get_confirm_token(response):
function save_response_content (line 73) | def save_response_content(response, destination, chunk_size=32*1024):
function unzip (line 81) | def unzip(filepath):
function download_celeb_a (line 88) | def download_celeb_a(dirpath):
function _list_categories (line 109) | def _list_categories(tag):
function _download_lsun (line 114) | def _download_lsun(out_dir, category, set_name, tag):
function download_lsun (line 127) | def download_lsun(dirpath):
function download_mnist (line 144) | def download_mnist(dirpath):
function prepare_data_dir (line 167) | def prepare_data_dir(path = './data'):
FILE: main.py
function main (line 42) | def main(_):
FILE: model.py
function conv_out_size_same (line 14) | def conv_out_size_same(size, stride):
function gen_random (line 17) | def gen_random(mode, size):
class DCGAN (line 23) | class DCGAN(object):
method __init__ (line 24) | def __init__(self, sess, input_height=108, input_width=108, crop=True,
method build_model (line 106) | def build_model(self):
method train (line 163) | def train(self, config):
method discriminator (line 333) | def discriminator(self, image, y=None, reuse=False):
method generator (line 364) | def generator(self, z, y=None):
method sampler (line 423) | def sampler(self, z, y=None):
method load_mnist (line 475) | def load_mnist(self):
method model_dir (line 513) | def model_dir(self):
method save (line 518) | def save(self, checkpoint_dir, step, filename='model', ckpt=True, froz...
method load (line 538) | def load(self, checkpoint_dir):
FILE: ops.py
function concat (line 23) | def concat(tensors, axis, *args, **kwargs):
function concat (line 26) | def concat(tensors, axis, *args, **kwargs):
class batch_norm (line 29) | class batch_norm(object):
method __init__ (line 30) | def __init__(self, epsilon=1e-5, momentum = 0.9, name="batch_norm"):
method __call__ (line 36) | def __call__(self, x, train=True):
function conv_cond_concat (line 45) | def conv_cond_concat(x, y):
function conv2d (line 52) | def conv2d(input_, output_dim,
function deconv2d (line 65) | def deconv2d(input_, output_shape,
function lrelu (line 90) | def lrelu(x, leak=0.2, name="lrelu"):
function linear (line 93) | def linear(input_, output_size, scope=None, stddev=0.02, bias_start=0.0,...
FILE: utils.py
function expand_path (line 27) | def expand_path(path):
function timestamp (line 30) | def timestamp(s='%Y%m%d.%H%M%S', ts=None):
function show_all_variables (line 35) | def show_all_variables():
function get_image (line 39) | def get_image(image_path, input_height, input_width,
function save_images (line 46) | def save_images(images, size, image_path):
function imread (line 49) | def imread(path, grayscale = False):
function merge_images (line 59) | def merge_images(images, size):
function merge (line 62) | def merge(images, size):
function imsave (line 83) | def imsave(images, size, path):
function center_crop (line 87) | def center_crop(x, crop_h, crop_w,
function transform (line 97) | def transform(image, input_height, input_width,
function inverse_transform (line 107) | def inverse_transform(images):
function to_json (line 110) | def to_json(output_path, *layers):
function make_gif (line 173) | def make_gif(images, fname, duration=2, true_image=False):
function visualize (line 190) | def visualize(sess, dcgan, config, option, sample_dir='samples'):
function image_manifold_size (line 264) | def image_manifold_size(num_images):
FILE: web/app.py
function index (line 7) | def index():
FILE: web/js/app.js
function rgb2hex (line 8) | function rgb2hex(rgb){
function cloneCanvas (line 76) | function cloneCanvas(oldCanvas) {
function deactivate (line 228) | function deactivate($el) {
function activate (line 233) | function activate($el) {
function disable (line 238) | function disable($el) {
function enable (line 243) | function enable($el) {
function isEnabled (line 248) | function isEnabled($el) {
function trackEvent (line 253) | function trackEvent(e, url) {
function getCoordinates (line 258) | function getCoordinates(e) {
function mouseDownCallback (line 272) | function mouseDownCallback(e) {
function mouseMoveCallback (line 280) | function mouseMoveCallback(e) {
function mouseUpCallback (line 288) | function mouseUpCallback() {
function collapseNavbar (line 341) | function collapseNavbar() {
FILE: web/js/convnet.js
function average (line 39) | function average(data, nChannel){
function standardDeviation (line 51) | function standardDeviation(v, nChannel){
function assert (line 148) | function assert(condition, message) {
function tanh (line 1474) | function tanh(x) {
Condensed preview — 16 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (200K chars).
[
{
"path": ".gitignore",
"chars": 989,
"preview": "# 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# Creat"
},
{
"path": "LICENSE",
"chars": 1078,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2016 Taehoon Kim\n\nPermission is hereby granted, free of charge, to any person obtai"
},
{
"path": "README.md",
"chars": 3718,
"preview": "# DCGAN in Tensorflow\n\nTensorflow implementation of [Deep Convolutional Generative Adversarial Networks](http://arxiv.or"
},
{
"path": "download.py",
"chars": 5375,
"preview": "\"\"\"\nModification of https://github.com/stanfordnlp/treelstm/blob/master/scripts/download.py\n\nDownloads the following:\n- "
},
{
"path": "main.py",
"chars": 6885,
"preview": "import os\nimport scipy.misc\nimport numpy as np\nimport json\n\nfrom model import DCGAN\nfrom utils import pp, visualize, to_"
},
{
"path": "model.py",
"chars": 21394,
"preview": "from __future__ import division\nfrom __future__ import print_function\nimport os\nimport time\nimport math\nfrom glob import"
},
{
"path": "ops.py",
"chars": 3904,
"preview": "import math\nimport numpy as np \nimport tensorflow as tf\n\nfrom tensorflow.python.framework import ops\n\nfrom utils import "
},
{
"path": "utils.py",
"chars": 9363,
"preview": "\"\"\"\nSome codes from https://github.com/Newmu/dcgan_code\n\"\"\"\nfrom __future__ import division\nimport math\nimport json\nimpo"
},
{
"path": "web/app.py",
"chars": 283,
"preview": "from flask import Flask\nfrom flask import render_template\n\napp = Flask(__name__, template_folder=\"./\", static_folder='./"
},
{
"path": "web/css/fakeLoader.css",
"chars": 7841,
"preview": "/**********************\n *CSS Animations by:\n *http://codepen.io/vivinantony\n***********************/\n.spinner1 {\n widt"
},
{
"path": "web/css/main.css",
"chars": 11611,
"preview": "canvas {\n background-color: white;\n}\n\n#colors {\n overflow: hidden;\n width: 90px;\n margin-left: 30px;\n flo"
},
{
"path": "web/index.html",
"chars": 26263,
"preview": "<!DOCTYPE html>\n<html>\n\n<head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n "
},
{
"path": "web/js/app.js",
"chars": 11727,
"preview": "\nwindow.mobilecheck = function() {\n var check = false;\n (function(a){if(/(android|bb\\d+|meego).+mobile|avantgo|bada\\/|"
},
{
"path": "web/js/convnet.js",
"chars": 81839,
"preview": "var convnetjs = convnetjs || { REVISION: 'ALPHA' };\n(function(global) {\n \"use strict\";\n\n // Random number utilities\n "
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the carpedm20/DCGAN-tensorflow GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 16 files (32.4 MB), approximately 54.0k tokens, and a symbol index with 69 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.