Advertisement
Guest User

Untitled

a guest
Jan 23rd, 2017
100
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.63 KB | None | 0 0
  1. import tensorflow as tf
  2. import numpy as np
  3. import os
  4.  
  5. import zconfig
  6. import utils
  7.  
  8. class DenoisingAutoencoder(object):
  9.  
  10. """ Implementation of Denoising Autoencoders using TensorFlow.
  11. The interface of the class is sklearn-like.
  12. """
  13.  
  14. def __init__(self, model_name='dae', n_components=256, main_dir='dae/', enc_act_func='tanh',
  15. dec_act_func='none', loss_func='mean_squared', num_epochs=10, batch_size=10, dataset='mnist',
  16. xavier_init=1, opt='gradient_descent', learning_rate=0.01, momentum=0.5, corr_type='none',
  17. corr_frac=0., verbose=1, seed=-1):
  18. """
  19. :param main_dir: main directory to put the models, data and summary directories
  20. :param n_components: number of hidden units
  21. :param enc_act_func: Activation function for the encoder. ['tanh', 'sigmoid']
  22. :param dec_act_func: Activation function for the decoder. ['tanh', 'sigmoid']
  23. :param loss_func: Loss function. ['mean_squared', 'cross_entropy']
  24. :param xavier_init: Value of the constant for xavier weights initialization
  25. :param opt: Which tensorflow optimizer to use. ['gradient_descent', 'momentum', 'ada_grad']
  26. :param learning_rate: Initial learning rate
  27. :param momentum: Momentum parameter
  28. :param corr_type: Type of input corruption. ["none", "masking", "salt_and_pepper"]
  29. :param corr_frac: Fraction of the input to corrupt.
  30. :param verbose: Level of verbosity. 0 - silent, 1 - print accuracy.
  31. :param num_epochs: Number of epochs
  32. :param batch_size: Size of each mini-batch
  33. :param dataset: Optional name for the dataset.
  34. :param seed: positive integer for seeding random generators. Ignored if < 0.
  35. """
  36.  
  37. self.model_name = model_name
  38. self.n_components = n_components
  39. self.main_dir = main_dir
  40. self.enc_act_func = enc_act_func
  41. self.dec_act_func = dec_act_func
  42. self.loss_func = loss_func
  43. self.num_epochs = num_epochs
  44. self.batch_size = batch_size
  45. self.dataset = dataset
  46. self.xavier_init = xavier_init
  47. self.opt = opt
  48. self.learning_rate = learning_rate
  49. self.momentum = momentum
  50. self.corr_type = corr_type
  51. self.corr_frac = corr_frac
  52. self.verbose = verbose
  53. self.seed = seed
  54.  
  55. if self.seed >= 0:
  56. np.random.seed(self.seed)
  57. tf.set_random_seed(self.seed)
  58.  
  59. self.models_dir, self.data_dir, self.tf_summary_dir = self._create_data_directories()
  60. self.model_path = self.models_dir + self.model_name
  61.  
  62. self.input_data = None
  63. self.input_data_corr = None
  64.  
  65. self.W_ = None
  66. self.bh_ = None
  67. self.bv_ = None
  68.  
  69. self.encode = None
  70. self.decode = None
  71.  
  72. self.train_step = None
  73. self.cost = None
  74.  
  75. self.tf_session = None
  76. self.tf_merged_summaries = None
  77. self.tf_summary_writer = None
  78. self.tf_saver = None
  79.  
  80. def fit(self, train_set, validation_set=None, restore_previous_model=False):
  81. """ Fit the model to the data.
  82.  
  83. :param train_set: Training data.
  84. :param validation_set: optional, default None. Validation data.
  85. :param restore_previous_model:
  86. if true, a previous trained model
  87. with the same name of this model is restored from disk to continue training.
  88.  
  89. :return: self
  90. """
  91. n_features = train_set.shape[1]
  92.  
  93. self._build_model(n_features)
  94.  
  95. with tf.Session() as self.tf_session:
  96.  
  97. self._initialize_tf_utilities_and_ops(restore_previous_model)
  98. self._train_model(train_set, validation_set)
  99. self.tf_saver.save(self.tf_session, self.models_dir + self.model_name)
  100.  
  101. def _initialize_tf_utilities_and_ops(self, restore_previous_model):
  102.  
  103. """ Initialize TensorFlow operations: summaries, init operations, saver, summary_writer.
  104. Restore a previously trained model if the flag restore_previous_model is true.
  105. """
  106.  
  107. self.tf_merged_summaries = tf.merge_all_summaries()
  108. init_op = tf.initialize_all_variables()
  109. self.tf_saver = tf.train.Saver()
  110.  
  111. self.tf_session.run(init_op)
  112.  
  113. if restore_previous_model:
  114. self.tf_saver.restore(self.tf_session, self.model_path)
  115.  
  116. self.tf_summary_writer = tf.train.SummaryWriter(self.tf_summary_dir, self.tf_session.graph_def)
  117.  
  118. def _train_model(self, train_set, validation_set):
  119.  
  120. """Train the model.
  121.  
  122. :param train_set: training set
  123. :param validation_set: validation set. optional, default None
  124.  
  125. :return: self
  126. """
  127.  
  128. corruption_ratio = np.round(self.corr_frac * train_set.shape[1]).astype(np.int)
  129.  
  130. for i in range(self.num_epochs):
  131.  
  132. self._run_train_step(train_set, corruption_ratio)
  133.  
  134. if i % 5 == 0:
  135. if validation_set is not None:
  136. self._run_validation_error_and_summaries(i, validation_set)
  137.  
  138. def _run_train_step(self, train_set, corruption_ratio):
  139.  
  140. """ Run a training step. A training step is made by randomly corrupting the training set,
  141. randomly shuffling it, divide it into batches and run the optimizer for each batch.
  142.  
  143. :param train_set: training set
  144. :param corruption_ratio: fraction of elements to corrupt
  145.  
  146. :return: self
  147. """
  148. x_corrupted = self._corrupt_input(train_set, corruption_ratio)
  149.  
  150. shuff = zip(train_set, x_corrupted)
  151. np.random.shuffle(shuff)
  152.  
  153. batches = [_ for _ in utils.gen_batches(shuff, self.batch_size)]
  154.  
  155. for batch in batches:
  156. x_batch, x_corr_batch = zip(*batch)
  157. tr_feed = {self.input_data: x_batch, self.input_data_corr: x_corr_batch}
  158. self.tf_session.run(self.train_step, feed_dict=tr_feed)
  159.  
  160. def _corrupt_input(self, data, v):
  161.  
  162. """ Corrupt a fraction 'v' of 'data' according to the
  163. noise method of this autoencoder.
  164. :return: corrupted data
  165. """
  166.  
  167. if self.corr_type == 'masking':
  168. x_corrupted = utils.masking_noise(data, v)
  169.  
  170. elif self.corr_type == 'salt_and_pepper':
  171. x_corrupted = utils.salt_and_pepper_noise(data, v)
  172.  
  173. elif self.corr_type == 'none':
  174. x_corrupted = data
  175.  
  176. else:
  177. x_corrupted = None
  178.  
  179. return x_corrupted
  180.  
  181. def _run_validation_error_and_summaries(self, epoch, validation_set):
  182.  
  183. """ Run the summaries and error computation on the validation set.
  184.  
  185. :param epoch: current epoch
  186. :param validation_set: validation data
  187.  
  188. :return: self
  189. """
  190.  
  191. vl_feed = {self.input_data: validation_set, self.input_data_corr: validation_set}
  192. result = self.tf_session.run([self.tf_merged_summaries, self.cost], feed_dict=vl_feed)
  193. summary_str = result[0]
  194. err = result[1]
  195.  
  196. self.tf_summary_writer.add_summary(summary_str, epoch)
  197.  
  198. if self.verbose == 1:
  199. print("Validation cost at step %s: %s" % (epoch, err))
  200.  
  201. def _build_model(self, n_features):
  202. """ Creates the computational graph.
  203.  
  204. :type n_features: int
  205. :param n_features: Number of features.
  206.  
  207. :return: self
  208. """
  209.  
  210. self.input_data, self.input_data_corr = self._create_placeholders(n_features)
  211. self.W_, self.bh_, self.bv_ = self._create_variables(n_features)
  212.  
  213. self._create_encode_layer()
  214. self._create_decode_layer()
  215.  
  216. self._create_cost_function_node()
  217. self._create_train_step_node()
  218.  
  219. def _create_placeholders(self, n_features):
  220.  
  221. """ Create the TensorFlow placeholders for the model.
  222.  
  223. :return: tuple(input_data(shape(None, n_features)),
  224. input_data_corr(shape(None, n_features)))
  225. """
  226.  
  227. input_data = tf.placeholder('float', [None, n_features], name='x-input')
  228. input_data_corr = tf.placeholder('float', [None, n_features], name='x-corr-input')
  229.  
  230. return input_data, input_data_corr
  231.  
  232. def _create_variables(self, n_features):
  233.  
  234. """ Create the TensorFlow variables for the model.
  235.  
  236. :return: tuple(weights(shape(n_features, n_components)),
  237. hidden bias(shape(n_components)),
  238. visible bias(shape(n_features)))
  239. """
  240.  
  241. W_ = tf.Variable(utils.xavier_init(n_features, self.n_components, self.xavier_init), name='enc-w')
  242. bh_ = tf.Variable(tf.zeros([self.n_components]), name='hidden-bias')
  243. bv_ = tf.Variable(tf.zeros([n_features]), name='visible-bias')
  244.  
  245. return W_, bh_, bv_
  246.  
  247. def _create_encode_layer(self):
  248.  
  249. """ Create the encoding layer of the network.
  250. :return: self
  251. """
  252.  
  253. with tf.name_scope("W_x_bh"):
  254. if self.enc_act_func == 'sigmoid':
  255. self.encode = tf.nn.sigmoid(tf.matmul(self.input_data_corr, self.W_) + self.bh_)
  256.  
  257. elif self.enc_act_func == 'tanh':
  258. self.encode = tf.nn.tanh(tf.matmul(self.input_data_corr, self.W_) + self.bh_)
  259.  
  260. else:
  261. self.encode = None
  262.  
  263. def _create_decode_layer(self):
  264.  
  265. """ Create the decoding layer of the network.
  266. :return: self
  267. """
  268.  
  269. with tf.name_scope("Wg_y_bv"):
  270. if self.dec_act_func == 'sigmoid':
  271. self.decode = tf.nn.sigmoid(tf.matmul(self.encode, tf.transpose(self.W_)) + self.bv_)
  272.  
  273. elif self.dec_act_func == 'tanh':
  274. self.decode = tf.nn.tanh(tf.matmul(self.encode, tf.transpose(self.W_)) + self.bv_)
  275.  
  276. elif self.dec_act_func == 'none':
  277. self.decode = tf.matmul(self.encode, tf.transpose(self.W_)) + self.bv_
  278.  
  279. else:
  280. self.decode = None
  281.  
  282. def _create_cost_function_node(self):
  283.  
  284. """ create the cost function node of the network.
  285. :return: self
  286. """
  287.  
  288. with tf.name_scope("cost"):
  289. if self.loss_func == 'cross_entropy':
  290. self.cost = - tf.reduce_sum(self.input_data * tf.log(self.decode))
  291. _ = tf.scalar_summary("cross_entropy", self.cost)
  292.  
  293. elif self.loss_func == 'mean_squared':
  294. self.cost = tf.sqrt(tf.reduce_mean(tf.square(self.input_data - self.decode)))
  295. _ = tf.scalar_summary("mean_squared", self.cost)
  296.  
  297. else:
  298. self.cost = None
  299.  
  300. def _create_train_step_node(self):
  301.  
  302. """ create the training step node of the network.
  303. :return: self
  304. """
  305.  
  306. with tf.name_scope("train"):
  307. if self.opt == 'gradient_descent':
  308. self.train_step = tf.train.GradientDescentOptimizer(self.learning_rate).minimize(self.cost)
  309.  
  310. elif self.opt == 'ada_grad':
  311. self.train_step = tf.train.AdagradOptimizer(self.learning_rate).minimize(self.cost)
  312.  
  313. elif self.opt == 'momentum':
  314. self.train_step = tf.train.MomentumOptimizer(self.learning_rate, self.momentum).minimize(self.cost)
  315.  
  316. else:
  317. self.train_step = None
  318.  
  319. def transform(self, data, name='train', save=False):
  320. """ Transform data according to the model.
  321.  
  322. :param data: Data to transform
  323. :param name: Identifier for the data that is being encoded
  324. :param save: If true, save data to disk
  325.  
  326. :return: transformed data
  327. """
  328.  
  329. with tf.Session() as self.tf_session:
  330.  
  331. self.tf_saver.restore(self.tf_session, self.models_dir + self.model_name)
  332.  
  333. encoded_data = self.encode.eval({self.input_data_corr: data})
  334.  
  335. if save:
  336. np.save(self.data_dir + self.model_name + '-' + name, encoded_data)
  337.  
  338. return encoded_data
  339.  
  340. def load_model(self, shape, model_path):
  341. """ Restore a previously trained model from disk.
  342.  
  343. :param shape: tuple(n_features, n_components)
  344. :param model_path: path to the trained model
  345.  
  346. :return: self, the trained model
  347. """
  348. self.n_components = shape[1]
  349.  
  350. self._build_model(shape[0])
  351.  
  352. init_op = tf.initialize_all_variables()
  353.  
  354. self.tf_saver = tf.train.Saver()
  355.  
  356. with tf.Session() as self.tf_session:
  357.  
  358. self.tf_session.run(init_op)
  359.  
  360. self.tf_saver.restore(self.tf_session, model_path)
  361.  
  362. def get_model_parameters(self):
  363. """ Return the model parameters in the form of numpy arrays.
  364.  
  365. :return: model parameters
  366. """
  367. with tf.Session() as self.tf_session:
  368.  
  369. self.tf_saver.restore(self.tf_session, self.models_dir + self.model_name)
  370.  
  371. return {
  372. 'enc_w': self.W_.eval(),
  373. 'enc_b': self.bh_.eval(),
  374. 'dec_b': self.bv_.eval()
  375. }
  376.  
  377. def _create_data_directories(self):
  378.  
  379. """ Create the three directories for storing respectively the models,
  380. the data generated by training and the TensorFlow's summaries.
  381.  
  382. :return: tuple of strings(models_dir, data_dir, summary_dir)
  383. """
  384.  
  385. self.main_dir = self.main_dir + '/' if self.main_dir[-1] != '/' else self.main_dir
  386.  
  387. models_dir = zconfig.models_dir + self.main_dir
  388. data_dir = zconfig.data_dir + self.main_dir
  389. summary_dir = zconfig.summary_dir + self.main_dir
  390.  
  391. for d in [models_dir, data_dir, summary_dir]:
  392. if not os.path.isdir(d):
  393. os.mkdir(d)
  394.  
  395. return models_dir, data_dir, summary_dir
  396.  
  397. def get_weights_as_images(self, width, height, outdir='img/', max_images=10, model_path=None):
  398. """ Save the weights of this autoencoder as images, one image per hidden unit.
  399. Useful to visualize what the autoencoder has learned.
  400.  
  401. :type width: int
  402. :param width: Width of the images
  403.  
  404. :type height: int
  405. :param height: Height of the images
  406.  
  407. :type outdir: string, default 'data/sdae/img'
  408. :param outdir: Output directory for the images. This path is appended to self.data_dir
  409.  
  410. :type max_images: int, default 10
  411. :param max_images: Number of images to return.
  412. """
  413. assert max_images <= self.n_components
  414.  
  415. outdir = self.data_dir + outdir
  416.  
  417. if not os.path.isdir(outdir):
  418. os.mkdir(outdir)
  419.  
  420. with tf.Session() as self.tf_session:
  421.  
  422. if model_path is not None:
  423. self.tf_saver.restore(self.tf_session, model_path)
  424. else:
  425. self.tf_saver.restore(self.tf_session, self.models_dir + self.model_name)
  426.  
  427. enc_weights = self.W_.eval()
  428.  
  429. perm = np.random.permutation(self.n_components)[:max_images]
  430.  
  431. for p in perm:
  432.  
  433. enc_w = np.array([i[p] for i in enc_weights])
  434. image_path = outdir + self.model_name + '-enc_weights_{}.png'.format(p)
  435. utils.gen_image(enc_w, width, height, image_path)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement