Advertisement
Guest User

Untitled

a guest
Feb 7th, 2016
68
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.79 KB | None | 0 0
  1. import tensorflow as tf
  2. import numpy as np
  3. import os
  4.  
  5. import config
  6. import utils
  7.  
  8.  
  9. class DenoisingAutoencoder(object):
  10. """ Implementation of Denoising Autoencoders using TensorFlow.
  11. The interface of the class is sklearn-like.
  12. """
  13.  
  14. def __getattr__(self, name):
  15. """
  16. Does absolutely nothing, only for pycharm to stop complaining about missing attributes
  17. This function is *only* called when python fail to find certain attribute, so it always raises exception
  18. """
  19. raise AttributeError("Attribute %s is not part of %s class" % (name, self.__class__.__name__))
  20.  
  21. def __init__(self, **kwargs):
  22. """ Constructor.
  23.  
  24. :type directory_name: string, default 'dae'
  25. :param directory_name: Optional directory name for store models, data and summaries
  26. (appended to models_dir, data_dir and summary_dir in the config)
  27.  
  28. :type n_components: int
  29. :param n_components: number of hidden units.
  30.  
  31. :type tied_weights: boolean, default True
  32. :param tied_weights: Whether to use tied weights
  33.  
  34. :type enc_act_func: string, default 'tanh', ['sigmoid', 'tanh']
  35. :param enc_act_func: Activation function for the encoder.
  36.  
  37. :type dec_act_func: string, default 'tanh', ['sigmoid', 'tanh', 'none']
  38. :param dec_act_func: Activation function for the decoder.
  39.  
  40. :type loss_func: string, default 'mean_squared', ['cross_entropy', 'mean_squared']
  41. :param loss_func: Loss function.
  42.  
  43. :type xavier_init: int, default 1
  44. :param xavier_init: Value of the constant for xavier weights initialization
  45.  
  46. :type opt: string, default 'gradient_descent', ['gradient_descent', 'ada_grad', 'momentum']
  47. :param opt: Which tensorflow optimizer to use.
  48.  
  49. :type learning_rate: float, default 0.01
  50. :param learning_rate: Initial learning rate.
  51.  
  52. :type momentum: float, default 0.5
  53. :param momentum: 'Momentum parameter.
  54.  
  55. :type corr_type: string, default 'none'
  56. :param corr_type: Type of input corruption. ["none", "masking", "salt_and_pepper"]
  57.  
  58. :type corr_frac: float, default 0.0
  59. :param corr_frac: Fraction of the input to corrupt.
  60.  
  61. :type verbose: int, default 0
  62. :param verbose: Level of verbosity. 0 - silent, 1 - print accuracy.
  63.  
  64. :type n_iter: int, default 10
  65. :param n_iter: Number of epochs
  66.  
  67. :type batch_size: int, default 10
  68. :param batch_size: Size of each mini-batch
  69.  
  70. :type dataset: string, default 'mnist'
  71. :param dataset: Optional name for the dataset.
  72.  
  73. :type seed: int, default -1
  74. :param seed: positive integer for seeding random generators. Ignored if < 0.
  75.  
  76. :return: self.
  77. """
  78.  
  79. prop_defaults = {
  80. 'model_name': '',
  81. 'n_components': 256,
  82. 'directory_name': 'dae/',
  83. 'enc_act_func': 'tanh',
  84. 'dec_act_func': 'none',
  85. 'loss_func': 'mean_squared',
  86. 'n_iter': 10,
  87. 'batch_size': 10,
  88. 'dataset': 'mnist',
  89. 'tied_weights': True,
  90. 'xavier_init': 1,
  91. 'opt': 'gradient_descent',
  92. 'learning_rate': 0.01,
  93. 'dropout': 0.5,
  94. 'momentum': 0.5,
  95. 'corr_type': 'none',
  96. 'corr_frac': 0.,
  97. 'verbose': 1,
  98. 'seed': -1,
  99. }
  100.  
  101. for (prop, default) in prop_defaults.iteritems():
  102. setattr(self, prop, kwargs.get(prop, default))
  103.  
  104. if self.seed >= 0:
  105. np.random.seed(self.seed)
  106. tf.set_random_seed(self.seed)
  107.  
  108. # Directories paths
  109. self.directory_name = self.directory_name + '/' if self.directory_name[-1] != '/' else self.directory_name
  110.  
  111. self.models_dir = config.models_dir + self.directory_name
  112. self.data_dir = config.data_dir + self.directory_name
  113. self.summary_dir = config.summary_dir + self.directory_name
  114.  
  115. for d in [self.models_dir, self.data_dir, self.summary_dir]:
  116. if not os.path.isdir(d):
  117. os.mkdir(d)
  118.  
  119. if self.model_name == '':
  120. # Assign model complete name
  121. self.model_name = 'dae-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}-{}'.format(
  122. self.dataset, self.n_components, self.corr_type, self.corr_frac, self.n_iter,
  123. self.batch_size, self.learning_rate, self.tied_weights, self.loss_func, self.enc_act_func,
  124. self.dec_act_func, self.opt)
  125.  
  126. # ########################### #
  127. # Computational graph nodes #
  128. # ########################### #
  129.  
  130. # Placeholders
  131. self.x = None # model original input
  132. self.x_corr = None # model corrupted input
  133.  
  134. # Model parameters
  135. self.Wf_ = None
  136. self.Wg_ = None
  137. self.bh_ = None
  138. self.bv_ = None
  139.  
  140. # Model values
  141. self.y = None # encoding phase output
  142. self.z = None # decoding phase output
  143.  
  144. # Model traning and evaluation
  145. self.train_step = None
  146. self.cost = None
  147.  
  148. # tensorflow objects
  149. self.sess = None
  150. self.saver = None
  151.  
  152. def _create_graph(self, n_features):
  153. """ Creates the computational graph.
  154.  
  155. :type n_features: int
  156. :param n_features: Number of features.
  157.  
  158. :return: self
  159. """
  160. # ################################### #
  161. # Computation Graph Specification #
  162. # ################################### #
  163.  
  164. # Symbolic variables
  165. self.x = tf.placeholder('float', [None, n_features], name='x-input')
  166. self.x_corr = tf.placeholder('float', [None, n_features], name='x-corr-input')
  167.  
  168. self.keep_prob = tf.placeholder('float')
  169.  
  170. # Biases
  171. self.bh_ = tf.Variable(tf.zeros([self.n_components]), name='hidden-bias')
  172. self.bv_ = tf.Variable(tf.zeros([n_features]), name='visible-bias')
  173.  
  174. # Weights
  175. self.Wf_ = tf.Variable(utils.xavier_init(n_features, self.n_components, self.xavier_init), name='enc-w')
  176.  
  177. if self.tied_weights:
  178. self.Wg_ = tf.transpose(self.Wf_)
  179.  
  180. else:
  181. self.Wg_ = tf.Variable(utils.xavier_init(n_features, self.n_components, self.xavier_init), name='dec-w')
  182.  
  183. # ############ #
  184. # Encoding #
  185. # ############ #
  186. with tf.name_scope("Wf_x_bh"):
  187. if self.enc_act_func == 'sigmoid':
  188. self.y = tf.nn.dropout(tf.nn.sigmoid(tf.matmul(self.x_corr, self.Wf_) + self.bh_), self.keep_prob)
  189.  
  190. elif self.enc_act_func == 'tanh':
  191. self.y = tf.nn.dropout(tf.nn.tanh(tf.matmul(self.x_corr, self.Wf_) + self.bh_), self.keep_prob)
  192.  
  193. else: # cannot be reached, just for completeness
  194. self.y = None
  195.  
  196. # ############ #
  197. # Decoding #
  198. # ############ #
  199. with tf.name_scope("Wg_y_bv"):
  200. if self.dec_act_func == 'sigmoid':
  201. self.z = tf.nn.sigmoid(tf.matmul(self.y, self.Wg_) + self.bv_)
  202.  
  203. elif self.dec_act_func == 'tanh':
  204. self.z = tf.nn.tanh(tf.matmul(self.y, self.Wg_) + self.bv_)
  205.  
  206. elif self.dec_act_func == 'none':
  207. self.z = tf.matmul(self.y, self.Wg_) + self.bv_
  208.  
  209. else: # cannot be reached, just for completeness
  210. self.z = None
  211.  
  212. # ############### #
  213. # Summary Ops #
  214. # ############### #
  215. # Add summary ops to collect data
  216. _ = tf.histogram_summary("enc_weights", self.Wf_)
  217. _ = tf.histogram_summary("hid_biases", self.bh_)
  218. _ = tf.histogram_summary("vis_biases", self.bv_)
  219. _ = tf.histogram_summary("y", self.y)
  220. _ = tf.histogram_summary("z", self.z)
  221.  
  222. if not self.tied_weights:
  223. _ = tf.histogram_summary("dec_weights", self.Wg_)
  224.  
  225. # ######## #
  226. # Cost #
  227. # ######## #
  228. with tf.name_scope("cost"):
  229. if self.loss_func == 'cross_entropy':
  230. self.cost = - tf.reduce_sum(self.x * tf.log(self.z))
  231. _ = tf.scalar_summary("cross_entropy", self.cost)
  232.  
  233. elif self.loss_func == 'mean_squared':
  234. self.cost = tf.sqrt(tf.reduce_mean(tf.square(self.x - self.z)))
  235. _ = tf.scalar_summary("mean_squared", self.cost)
  236.  
  237. else: # cannot be reached, just for completeness
  238. self.cost = None
  239.  
  240. with tf.name_scope("train"):
  241. if self.opt == 'gradient_descent':
  242. self.train_step = tf.train.GradientDescentOptimizer(self.learning_rate).minimize(self.cost)
  243.  
  244. elif self.opt == 'ada_grad':
  245. self.train_step = tf.train.AdagradOptimizer(self.learning_rate).minimize(self.cost)
  246.  
  247. elif self.opt == 'momentum':
  248. self.train_step = tf.train.MomentumOptimizer(self.learning_rate, self.momentum).minimize(self.cost)
  249.  
  250. else: # cannot be reached, just for completeness
  251. self.train_step = None
  252.  
  253. def fit(self, trX, vlX=None, restore_previous_model=False):
  254. """ Fit the model to the data.
  255.  
  256. :type trX: array_like, shape (n_samples, n_features).
  257. :param trX: Training data.
  258.  
  259. :type vlX: array_like, shape (n_validation_samples, n_features).
  260. :param vlX: optional, default None. Validation data.
  261.  
  262. :return: self
  263. """
  264. n_features = trX.shape[1]
  265.  
  266. self._create_graph(n_features)
  267.  
  268. # Merge all the summaries
  269. merged = tf.merge_all_summaries()
  270. # Initialize variables
  271. init_op = tf.initialize_all_variables()
  272. # Add ops to save and restore all the variables
  273. self.saver = tf.train.Saver()
  274.  
  275. with tf.Session() as self.sess:
  276.  
  277. self.sess.run(init_op)
  278.  
  279. if restore_previous_model:
  280. # Restore previous model
  281. self.saver.restore(self.sess, self.models_dir + self.model_name)
  282. # Change model name
  283. self.model_name += '-restored{}'.format(self.n_iter)
  284.  
  285. # ################## #
  286. # Training phase #
  287. # ################## #
  288.  
  289. v = np.round(self.corr_frac * n_features).astype(np.int)
  290.  
  291. # Write the summaries to summary_dir
  292. writer = tf.train.SummaryWriter(self.summary_dir, self.sess.graph_def)
  293.  
  294. for i in range(self.n_iter):
  295.  
  296. # #################### #
  297. # Input Corruption #
  298. # #################### #
  299.  
  300. if self.corr_type == 'masking':
  301. x_corrupted = utils.masking_noise(trX, v)
  302.  
  303. elif self.corr_type == 'salt_and_pepper':
  304. x_corrupted = utils.salt_and_pepper_noise(trX, v)
  305.  
  306. else: # none, normal autoencoder
  307. x_corrupted = trX
  308.  
  309. # Randomly shuffle the input
  310. shuff = zip(trX, x_corrupted)
  311. np.random.shuffle(shuff)
  312.  
  313. # # Divide dataset into mini-batches
  314. batches = [_ for _ in utils.gen_batches(shuff, self.batch_size)]
  315.  
  316. # All the batches for each epoch
  317. for batch in batches:
  318. x_batch, x_corr_batch = zip(*batch)
  319. tr_feed = {self.x: x_batch, self.x_corr: x_corr_batch, self.keep_prob: self.dropout}
  320. self.sess.run(self.train_step, feed_dict=tr_feed)
  321.  
  322. # Record summary data
  323. if vlX is not None:
  324. vl_feed = {self.x: vlX, self.x_corr: vlX, self.keep_prob: 1.}
  325. result = self.sess.run([merged, self.cost], feed_dict=vl_feed)
  326. summary_str = result[0]
  327. err = result[1]
  328.  
  329. writer.add_summary(summary_str, i)
  330.  
  331. if self.verbose == 1:
  332. print("Validation cost at step %s: %s" % (i, err))
  333.  
  334. # Save trained model
  335. self.saver.save(self.sess, self.models_dir + self.model_name)
  336.  
  337. def transform(self, data, name='train', save=False):
  338. """ Transform data according to the model.
  339.  
  340. :type data: array_like
  341. :param data: Data to transform
  342.  
  343. :type name: string, default 'train'
  344. :param name: Identifier for the data that is being encoded
  345.  
  346. :type save: boolean, default 'False'
  347. :param save: If true, save data to disk
  348.  
  349. :return: transformed data
  350. """
  351.  
  352. with tf.Session() as self.sess:
  353.  
  354. # Restore trained model
  355. self.saver.restore(self.sess, self.models_dir + self.model_name)
  356.  
  357. # Return the output of the encoding layer
  358. encoded_data = self.y.eval({self.x_corr: data})
  359.  
  360. if save:
  361. # Save transformation to output file
  362. np.save(self.data_dir + self.model_name + '-' + name, encoded_data)
  363.  
  364. return encoded_data
  365.  
  366. def load_model(self, shape, model_path):
  367. """ Restore a previously trained model from disk.
  368.  
  369. :type shape: tuple
  370. :param shape: tuple(n_features, n_components)
  371.  
  372. :type model_path: string
  373. :param model_path: path to the trained model
  374.  
  375. :return: self, the trained model
  376. """
  377. self.n_components = shape[1]
  378.  
  379. self._create_graph(shape[0])
  380.  
  381. # Initialize variables
  382. init_op = tf.initialize_all_variables()
  383.  
  384. # Add ops to save and restore all the variables
  385. self.saver = tf.train.Saver()
  386.  
  387. with tf.Session() as self.sess:
  388.  
  389. self.sess.run(init_op)
  390.  
  391. # Restore previous model
  392. self.saver.restore(self.sess, model_path)
  393.  
  394. def get_model_parameters(self):
  395. """ Return the model parameters in the form of numpy arrays.
  396.  
  397. :return: model parameters
  398. """
  399. with tf.Session() as self.sess:
  400.  
  401. # Restore trained model
  402. self.saver.restore(self.sess, self.models_dir + self.model_name)
  403.  
  404. return {
  405. 'enc_w': self.Wf_.eval(),
  406. 'dec_w': self.Wg_.eval() if self.tied_weights else None,
  407. 'enc_b': self.bh_.eval(),
  408. 'dec_b': self.bv_.eval()
  409. }
  410.  
  411. def get_weights_as_images(self, width, height, outdir='img/', max_images=10, model_path=None):
  412. """ Save the weights of this autoencoder as images, one image per hidden unit.
  413. Useful to visualize what the autoencoder has learned.
  414.  
  415. :type width: int
  416. :param width: Width of the images
  417.  
  418. :type height: int
  419. :param height: Height of the images
  420.  
  421. :type outdir: string, default 'data/sdae/img'
  422. :param outdir: Output directory for the images. This path is appended to self.data_dir
  423.  
  424. :type max_images: int, default 10
  425. :param max_images: Number of images to return.
  426. """
  427. assert max_images <= self.n_components
  428.  
  429. outdir = self.data_dir + outdir
  430.  
  431. if not os.path.isdir(outdir):
  432. os.mkdir(outdir)
  433.  
  434. with tf.Session() as self.sess:
  435.  
  436. # Restore trained model
  437. if model_path is not None:
  438. self.saver.restore(self.sess, model_path)
  439. else:
  440. self.saver.restore(self.sess, self.models_dir + self.model_name)
  441.  
  442. # Extract encoding weights as numpy array
  443. enc_weights = self.Wf_.eval()
  444.  
  445. # Extract decoding weights as numpy arrays
  446. dec_weights = self.Wg_.eval() if not self.tied_weights else None
  447.  
  448. perm = np.random.permutation(self.n_components)[:max_images]
  449.  
  450. for p in perm:
  451.  
  452. enc_w = np.array([i[p] for i in enc_weights])
  453. image_path = outdir + self.model_name + '-enc_weights_{}.png'.format(p)
  454. utils.gen_image(enc_w, width, height, image_path)
  455.  
  456. if not self.tied_weights:
  457. dec_w = np.array([i[p] for i in dec_weights])
  458. image_path = outdir + self.model_name + '-dec_weights_{}.png'.format(p)
  459. utils.gen_image(dec_w, width, height, image_path)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement