Deeplearning Algorithms tutorial

谷歌的人工智能位于全球前列,在图像识别、语音识别、无人驾驶等技术上都已经落地。而百度实质意义上扛起了国内的人工智能的大旗,覆盖无人驾驶、智能助手、图像识别等许多层面。苹果业已开始全面拥抱机器学习,新产品进军家庭智能音箱并打造工作站级别Mac。另外,腾讯的深度学习平台Mariana已支持了微信语音识别的语音输入法、语音开放平台、长按语音消息转文本等产品,在微信图像识别中开始应用。全球前十大科技公司全部发力人工智能理论研究和应用的实现,虽然入门艰难,但是一旦入门,高手也就在你的不远处! AI的开发离不开算法那我们就接下来开始学习算法吧!

堆叠自动编码器(Stacked AutoEncoder)

自从Hinton 2006年的工作之后,越来越多的研究者开始关注各种自编码器模型相应的堆叠模型。实际上,自编码器(Auto-Encoder)是一个较早的概念了,比如Hinton等人在1986, 1989年的工作。

自编码器可以理解为一个试图去还原其原始输入的系统。

堆叠自动编码器(Stacked AutoEncoder) - 图1

图中,虚线蓝色框内就是一个自编码器模型,它由编码器(Encoder)和解码器(Decoder)两部分组成,本质上都是对输入信号做某种变换。 编码器将输入信号x变换成编码信号y,而解码器将编码y转换成输出信号堆叠自动编码器(Stacked AutoEncoder) - 图2,即

堆叠自动编码器(Stacked AutoEncoder) - 图3

而自编码器的目的是,让输出尽可能复现输入x.但是,这样问题就来了,如果f和g都是恒等映射,那就是堆叠自动编码器(Stacked AutoEncoder) - 图4,实际上事实确实如此,但这样的变换就没有什么用了,所以我们需要对中间信号y(也叫作“编码”)做一定的约束,这样,系统往往能学出很有趣的编码变换f和编码y。

在这里需要强调一点,对于自编码器,往往并不关系输出是什么,因常我们只需要关心中间层的编码,或者是从输入到编码的映射。在我们强迫编码y和输入x不同的情况下,系统还能够去复原原始信号x,那么说明编码y已经承载了原始数据的所有信息,但以一种不同的形式!这就是特征提取。这个是通过自动学出来的,事实上,自动学习原始数据的特征表达也是神经网络和深度学习的核心目的之一。

自编码器与神经网络,其中神经网络就是在对原始信号逐层地做非线性变换:

堆叠自动编码器(Stacked AutoEncoder) - 图5

该网络把输入层数据堆叠自动编码器(Stacked AutoEncoder) - 图6转换到中间层(隐层)堆叠自动编码器(Stacked AutoEncoder) - 图7,再转换到输出层堆叠自动编码器(Stacked AutoEncoder) - 图8。图中的每个节点代表数据的一个维度(偏置项图中未标出)。每两层之间的变换都是“线性变化”+“非线性激活”,用公式表示即为。图中的每个节点代表数据的一个维度(偏置项图中未标出)。每两层之间的变换都是“线性变化”+“非线性激活”,用公式表示为:

堆叠自动编码器(Stacked AutoEncoder) - 图9

神经网络通常可以用于分类,这样可以得道从输入层到输出层的变换函数。因此,我们需要定义一个目标函数来衡量当前的输出和真实结果的差异,利用该函数去逐步调整(如梯度下降)系统的参数堆叠自动编码器(Stacked AutoEncoder) - 图10,以使得整个网络尽可能去拟合训练数据。如果有正则约束的话,还同时要求模型尽量简单(以防止过拟合)。

但是自编码器怎么表示呢?

自编码器试图复现其原始输入,因此,在训练中,网络中的输出应与输入相同,即 y=x,因此,一个自编码器的输入、输出应有相同的结构,即:

堆叠自动编码器(Stacked AutoEncoder) - 图11

这里我们可以利用训练数据训练这个网络,等训练结束后,这个网络即学习出了堆叠自动编码器(Stacked AutoEncoder) - 图12的能力。对我们来说,此时的h是至关重要的,因为它是在尽量不损失信息量的情况下,对原始数据的另一种表达。结合神经网络的惯例,我们再将自编码器的公式表示如下:(假设激活函数是sigmoid,用s表示)

堆叠自动编码器(Stacked AutoEncoder) - 图13

其中,堆叠自动编码器(Stacked AutoEncoder) - 图14表示损失函数,结合数据的不同形式,可以是二次误差(squared error loss)或交叉熵误差(cross entropy loss)。如果堆叠自动编码器(Stacked AutoEncoder) - 图15,一般称为tied weights。

这里我们会给隐层加入一定的约束。从数据维度来看,常见以下两种情况:

  • n>p,即隐层维度小于输入数据维度。从x→h的变换是一种降维的操作,网络试图以更小的维度去描述原始数据而尽量不损失数据信息。实际上,当每两层之间的变换均为线性,且监督训练的误差是二次型误差时,该网络等价于PCA!

  • n<p ,即隐层维度大于输入数据维度。虽然我们同时约束h的表达尽量稀疏(有大量维度为0,未被激活),此时的编码器便是非常有名的“稀疏自编码器”。可为什么稀疏的表达就是好的?这是因为,有人尝试从人脑机理对比,人类神经系统在某一刺激下,大部分神经元是被抑制的。

学习过深度学习的都明白,深度学习中深度网络的重要性在于其能够逐层地学习原始数据的多种表达。每一层的都以底一层的表达为基础,但往往更抽象,更加适合复杂的分类等任务。

堆叠自编码器实际上就在做这样的事情,如前所述,单个自编码器通过虚构堆叠自动编码器(Stacked AutoEncoder) - 图16的三层网络,能够学习出一种特征变化堆叠自动编码器(Stacked AutoEncoder) - 图17(这里用θ表示变换的参数,包括W,b和激活函数)。实际上,当训练结束后,输出层就没什么意义了,我们一般会将其去掉的,即将自编码器表示为:

堆叠自动编码器(Stacked AutoEncoder) - 图18

自编码器模型这里表示为3层的神经网络,这是因为训练的需要,我们将原始数据作为假想的目标输出,以此构建监督误差来训练整个网络。等训练结束后,输出层就可以去掉了,而我们关心的只是从x到h的变换过程。

接下来的思路就很自然了——我们已经得到特征表达h,那么我们就可以将h再当做原始信息,训练一个新的自编码器,得到新的特征表达。

堆叠自编码器(Stacked Auto-Encoder, SAE),这里Stacked就是逐层垒叠的意思,跟“栈”有点像,当把多个自编码器Stack起来之后就是如下的过程:

堆叠自动编码器(Stacked AutoEncoder) - 图19

这里需要注意的是,整个网络的训练不是一蹴而就的,而是需要逐层进行。根据n,m,k结构,实际上我们是先训练网络n→m→n,得到n→m的变换,然后再训练 m→k→m,得到m→k的变换。最终堆叠成SAE,即为n→m→k的结果,整个过程就像一层层往上构建网络,这个就是非常有名的 layer-wise unsuperwised pre-training(逐层非监督预训练)。

稀疏自编码器如前所示,这种模型背后的思想是,高维而稀疏的表达是好的。一般而言h中哪些节点是被抑制的(对于sigmoid单元即输出为0),而是指定一个稀疏性参数ρ,,代表隐藏神经元的平均活跃程度(在训练集上取平均)。比如,当ρ=0.05时,可以认为隐层节点在95%的时间里都是被一直的,只有5%的机会被激活。实际上,为了满足这一条件,隐层神经元的活跃度需要接近于0。

既然要求平均激活度为ρ,那么我们只要引入一个度量,来衡量神经元i的实际激活度堆叠自动编码器(Stacked AutoEncoder) - 图20与期望激活度ρ之间的差异即可,然后将这个度量添加到目标函数作为正则,训练整个网络即可。那么,什么样的度量适合这个任务呢?了解过概率论、信息论基础的人应该清楚——相对熵,也就是KL散度(KL divergence)。因此,整个网络所添加的惩罚项:

堆叠自动编码器(Stacked AutoEncoder) - 图21

我们可以从下图(摘自UFLDL)中直观理解KL散度作为惩罚项的含义。图中假设平均激活度ρ=0.2。

堆叠自动编码器(Stacked AutoEncoder) - 图22

可以看出,当堆叠自动编码器(Stacked AutoEncoder) - 图23一旦偏离期望激活度ρ,这种误差便急剧增大,从而作为惩罚项添加到目标函数,指导整个网络学习出稀疏的特征表达。

降噪自编码器(Stacked Denoising Autoencoders)核心思想是,一个能够从中恢复出原始信号的表达未必是最好的,能够对“被污染/破坏”的原始数据编码、解码,然后还能恢复真正的原始数据,这样的特征才是好的。

假设原始数据x被我们“故意破坏”,比如加入高斯白噪,或者把某些维度数据抹掉,变成了堆叠自动编码器(Stacked AutoEncoder) - 图24,然后再对堆叠自动编码器(Stacked AutoEncoder) - 图25编码、解码,得到恢复信号堆叠自动编码器(Stacked AutoEncoder) - 图26,该恢复信号尽可能逼近未被污染的数据x。此时,监督训练的误差从堆叠自动编码器(Stacked AutoEncoder) - 图27变成了堆叠自动编码器(Stacked AutoEncoder) - 图28

降噪自编码器的系统结构如下图:

堆叠自动编码器(Stacked AutoEncoder) - 图29

深度学习兴起正式因为逐层预训练方法的提出,使得深度网络的训练成为可能。对于一个深度网络而言,这种逐层预训练的方法,正是前面介绍的这种堆叠自动编码器(Stacked AutoEncoder)。对于常见的分类任务,一般分为以下两个阶段:

  • 逐层预训练(layer-wise pre-training)
  • 微调(fune-tuning )

上面讲述的的自动编码器,本质上都是非监督学习,自动编码器各层的输出都是原始数据的不同表达。对于分类任务,往往在自动编码器顶端再添加一分类层(如Softmax层),并结合有标注的训练数据,在误差函数的指导下,对系统的参数进行微调,以使得整个网络能够完成所需的分类任务。

堆叠自动编码器(Stacked AutoEncoder) - 图30

对于微调过程,即可以只调整分类层的参数(此时相当于把整个SAE当做一个feature extractor),也可以调整整个网络的参数(适合训练数据量比较大的情况)。

但是为什么训练稀疏自编码器为什么一般都是三层的结构,实际上这里的三层是指训练单个自编码器所假想的3层神经网络,这对任何基于神经网络的编码器都是这样。多层的稀疏自编码器自然是有的,只不过是通过layer-wise pre-training这种方式逐层堆叠起来的,而不是直接去训练多层的网络。

实际上,这正是在训练深层神经网络中遇到的问题。直接去训练一个深层的自编码器,其实本质上就是在做深度网络的训练,由于梯度扩散等问题,这样的网络往往根本无法训练。这倒不是因为会破坏稀疏性等原因,只要网络能够训练,对模型施加的约束总能得到相应的结果。

但是为什么逐层预训练就可以使得深度网络的训练成为可能了呢?其实一个直观的解释是,预训练好的网络在一定程度上拟合了训练数据的结构,这使得整个网络的初始值是在一个合适的状态,便于有监督阶段加快迭代收敛。

应用示例

  1. # This piece of software is bound by The MIT License (MIT)
  2. # Copyright (c) 2014 Siddharth Agrawal
  3. # Code written by : Siddharth Agrawal
  4. # Email ID : siddharth.950@gmail.com
  5. import struct
  6. import array
  7. import numpy
  8. import math
  9. import time
  10. import scipy.io
  11. import scipy.optimize
  12. """ Returns elementwise sigmoid output of input array """
  13. def sigmoid(x):
  14. return (1 / (1 + numpy.exp(-x)))
  15. """ Returns the groundtruth matrix for a set of labels """
  16. def getGroundTruth(labels):
  17. """ Prepare data needed to construct groundtruth matrix """
  18. labels = numpy.array(labels).flatten()
  19. data = numpy.ones(len(labels))
  20. indptr = numpy.arange(len(labels)+1)
  21. """ Compute the groundtruth matrix and return """
  22. ground_truth = scipy.sparse.csr_matrix((data, labels, indptr))
  23. ground_truth = numpy.transpose(ground_truth.todense())
  24. return ground_truth
  25. """ The Sparse Autoencoder class """
  26. class SparseAutoencoder(object):
  27. """ Initialization of Autoencoder object """
  28. def __init__(self, visible_size, hidden_size, rho, lamda, beta):
  29. """ Initialize parameters of the Autoencoder object """
  30. self.visible_size = visible_size # number of input units
  31. self.hidden_size = hidden_size # number of hidden units
  32. self.rho = rho # desired average activation of hidden units
  33. self.lamda = lamda # weight decay parameter
  34. self.beta = beta # weight of sparsity penalty term
  35. """ Set limits for accessing 'theta' values """
  36. self.limit0 = 0
  37. self.limit1 = hidden_size * visible_size
  38. self.limit2 = 2 * hidden_size * visible_size
  39. self.limit3 = 2 * hidden_size * visible_size + hidden_size
  40. self.limit4 = 2 * hidden_size * visible_size + hidden_size + visible_size
  41. """ Initialize Neural Network weights randomly
  42. W1, W2 values are chosen in the range [-r, r] """
  43. r = math.sqrt(6) / math.sqrt(visible_size + hidden_size + 1)
  44. rand = numpy.random.RandomState(int(time.time()))
  45. W1 = numpy.asarray(rand.uniform(low = -r, high = r, size = (hidden_size, visible_size)))
  46. W2 = numpy.asarray(rand.uniform(low = -r, high = r, size = (visible_size, hidden_size)))
  47. """ Bias values are initialized to zero """
  48. b1 = numpy.zeros((hidden_size, 1))
  49. b2 = numpy.zeros((visible_size, 1))
  50. """ Create 'theta' by unrolling W1, W2, b1, b2 """
  51. self.theta = numpy.concatenate((W1.flatten(), W2.flatten(),
  52. b1.flatten(), b2.flatten()))
  53. """ Returns gradient of 'theta' using Backpropagation algorithm """
  54. def sparseAutoencoderCost(self, theta, input):
  55. """ Extract weights and biases from 'theta' input """
  56. W1 = theta[self.limit0 : self.limit1].reshape(self.hidden_size, self.visible_size)
  57. W2 = theta[self.limit1 : self.limit2].reshape(self.visible_size, self.hidden_size)
  58. b1 = theta[self.limit2 : self.limit3].reshape(self.hidden_size, 1)
  59. b2 = theta[self.limit3 : self.limit4].reshape(self.visible_size, 1)
  60. """ Compute output layers by performing a feedforward pass
  61. Computation is done for all the training inputs simultaneously """
  62. hidden_layer = sigmoid(numpy.dot(W1, input) + b1)
  63. output_layer = sigmoid(numpy.dot(W2, hidden_layer) + b2)
  64. """ Estimate the average activation value of the hidden layers """
  65. rho_cap = numpy.sum(hidden_layer, axis = 1) / input.shape[1]
  66. """ Compute intermediate difference values using Backpropagation algorithm """
  67. diff = output_layer - input
  68. sum_of_squares_error = 0.5 * numpy.sum(numpy.multiply(diff, diff)) / input.shape[1]
  69. weight_decay = 0.5 * self.lamda * (numpy.sum(numpy.multiply(W1, W1)) +
  70. numpy.sum(numpy.multiply(W2, W2)))
  71. KL_divergence = self.beta * numpy.sum(self.rho * numpy.log(self.rho / rho_cap) +
  72. (1 - self.rho) * numpy.log((1 - self.rho) / (1 - rho_cap)))
  73. cost = sum_of_squares_error + weight_decay + KL_divergence
  74. KL_div_grad = self.beta * (-(self.rho / rho_cap) + ((1 - self.rho) / (1 - rho_cap)))
  75. del_out = numpy.multiply(diff, numpy.multiply(output_layer, 1 - output_layer))
  76. del_hid = numpy.multiply(numpy.dot(numpy.transpose(W2), del_out) + numpy.transpose(numpy.matrix(KL_div_grad)),
  77. numpy.multiply(hidden_layer, 1 - hidden_layer))
  78. """ Compute the gradient values by averaging partial derivatives
  79. Partial derivatives are averaged over all training examples """
  80. W1_grad = numpy.dot(del_hid, numpy.transpose(input))
  81. W2_grad = numpy.dot(del_out, numpy.transpose(hidden_layer))
  82. b1_grad = numpy.sum(del_hid, axis = 1)
  83. b2_grad = numpy.sum(del_out, axis = 1)
  84. W1_grad = W1_grad / input.shape[1] + self.lamda * W1
  85. W2_grad = W2_grad / input.shape[1] + self.lamda * W2
  86. b1_grad = b1_grad / input.shape[1]
  87. b2_grad = b2_grad / input.shape[1]
  88. """ Transform numpy matrices into arrays """
  89. W1_grad = numpy.array(W1_grad)
  90. W2_grad = numpy.array(W2_grad)
  91. b1_grad = numpy.array(b1_grad)
  92. b2_grad = numpy.array(b2_grad)
  93. """ Unroll the gradient values and return as 'theta' gradient """
  94. theta_grad = numpy.concatenate((W1_grad.flatten(), W2_grad.flatten(),
  95. b1_grad.flatten(), b2_grad.flatten()))
  96. return [cost, theta_grad]
  97. """ The Softmax Regression class """
  98. class SoftmaxRegression(object):
  99. """ Initialization of Regressor object """
  100. def __init__(self, input_size, num_classes, lamda):
  101. """ Initialize parameters of the Regressor object """
  102. self.input_size = input_size # input vector size
  103. self.num_classes = num_classes # number of classes
  104. self.lamda = lamda # weight decay parameter
  105. """ Randomly initialize the class weights """
  106. rand = numpy.random.RandomState(int(time.time()))
  107. self.theta = 0.005 * numpy.asarray(rand.normal(size = (num_classes*input_size, 1)))
  108. """ Returns the cost and gradient of 'theta' at a particular 'theta' """
  109. def softmaxCost(self, theta, input, labels):
  110. """ Compute the groundtruth matrix """
  111. ground_truth = getGroundTruth(labels)
  112. """ Reshape 'theta' for ease of computation """
  113. theta = theta.reshape(self.num_classes, self.input_size)
  114. """ Compute the class probabilities for each example """
  115. theta_x = numpy.dot(theta, input)
  116. hypothesis = numpy.exp(theta_x)
  117. probabilities = hypothesis / numpy.sum(hypothesis, axis = 0)
  118. """ Compute the traditional cost term """
  119. cost_examples = numpy.multiply(ground_truth, numpy.log(probabilities))
  120. traditional_cost = -(numpy.sum(cost_examples) / input.shape[1])
  121. """ Compute the weight decay term """
  122. theta_squared = numpy.multiply(theta, theta)
  123. weight_decay = 0.5 * self.lamda * numpy.sum(theta_squared)
  124. """ Add both terms to get the cost """
  125. cost = traditional_cost + weight_decay
  126. """ Compute and unroll 'theta' gradient """
  127. theta_grad = -numpy.dot(ground_truth - probabilities, numpy.transpose(input))
  128. theta_grad = theta_grad / input.shape[1] + self.lamda * theta
  129. theta_grad = numpy.array(theta_grad)
  130. theta_grad = theta_grad.flatten()
  131. return [cost, theta_grad]
  132. """ Loads the images from the provided file name """
  133. def loadMNISTImages(file_name):
  134. """ Open the file """
  135. image_file = open(file_name, 'rb')
  136. """ Read header information from the file """
  137. head1 = image_file.read(4)
  138. head2 = image_file.read(4)
  139. head3 = image_file.read(4)
  140. head4 = image_file.read(4)
  141. """ Format the header information for useful data """
  142. num_examples = struct.unpack('>I', head2)[0]
  143. num_rows = struct.unpack('>I', head3)[0]
  144. num_cols = struct.unpack('>I', head4)[0]
  145. """ Initialize dataset as array of zeros """
  146. dataset = numpy.zeros((num_rows*num_cols, num_examples))
  147. """ Read the actual image data """
  148. images_raw = array.array('B', image_file.read())
  149. image_file.close()
  150. """ Arrange the data in columns """
  151. for i in range(num_examples):
  152. limit1 = num_rows * num_cols * i
  153. limit2 = num_rows * num_cols * (i + 1)
  154. dataset[:, i] = images_raw[limit1 : limit2]
  155. """ Normalize and return the dataset """
  156. return dataset / 255
  157. """ Loads the image labels from the provided file name """
  158. def loadMNISTLabels(file_name):
  159. """ Open the file """
  160. label_file = open(file_name, 'rb')
  161. """ Read header information from the file """
  162. head1 = label_file.read(4)
  163. head2 = label_file.read(4)
  164. """ Format the header information for useful data """
  165. num_examples = struct.unpack('>I', head2)[0]
  166. """ Initialize data labels as array of zeros """
  167. labels = numpy.zeros((num_examples, 1), dtype = numpy.int)
  168. """ Read the label data """
  169. labels_raw = array.array('b', label_file.read())
  170. label_file.close()
  171. """ Copy and return the label data """
  172. labels[:, 0] = labels_raw[:]
  173. return labels
  174. """ Returns the hidden layer activations of the Autoencoder """
  175. def feedForwardAutoencoder(theta, hidden_size, visible_size, input):
  176. """ Define limits to access useful data """
  177. limit0 = 0
  178. limit1 = hidden_size * visible_size
  179. limit2 = 2 * hidden_size * visible_size
  180. limit3 = 2 * hidden_size * visible_size + hidden_size
  181. """ Access W1 and b1 from 'theta' """
  182. W1 = theta[limit0 : limit1].reshape(hidden_size, visible_size)
  183. b1 = theta[limit2 : limit3].reshape(hidden_size, 1)
  184. """ Compute the hidden layer activations """
  185. hidden_layer = 1 / (1 + numpy.exp(-(numpy.dot(W1, input) + b1)))
  186. return hidden_layer
  187. """ Returns a row of Stacked Autoencoder parameters """
  188. def stack2Params(stack):
  189. """ Initialize an empty list of parameters """
  190. params = []
  191. num_layers = len(stack) / 2
  192. """ For each layer in the neural network, append the corresponding parameters """
  193. for i in range(num_layers):
  194. params = numpy.concatenate((params, numpy.array(stack[i, "W"]).flatten()))
  195. params = numpy.concatenate((params, numpy.array(stack[i, "b"]).flatten()))
  196. return params
  197. """ Returns a stack of Stacked Autoencoder parameters """
  198. def params2Stack(params, net_config):
  199. """ Initialize an empty stack """
  200. stack = {}
  201. limit0 = 0
  202. for i in range(len(net_config)-2):
  203. """ Calculate limits of layer parameters, using neural network config """
  204. limit1 = limit0 + net_config[i] * net_config[i+1]
  205. limit2 = limit1 + net_config[i+1]
  206. """ Extract layer parameters, and store in the stack """
  207. stack[i, "W"] = params[limit0 : limit1].reshape(net_config[i+1], net_config[i])
  208. stack[i, "b"] = params[limit1 : limit2].reshape(net_config[i+1], 1)
  209. limit0 = limit2
  210. return stack
  211. """ Function for finetuning the Stacked Autoencoder """
  212. def stackedAutoencoderCost(theta, net_config, lamda, data, labels):
  213. """ Calculate limits for Softmax parameters """
  214. input_size = net_config[-2]
  215. num_classes = net_config[-1]
  216. limit0 = 0
  217. limit1 = num_classes * input_size
  218. """ Extract Softmax and layer parameters """
  219. softmax_theta = theta[limit0 : limit1].reshape(num_classes, input_size)
  220. stack = params2Stack(theta[limit1 :], net_config)
  221. num_layers = len(stack) / 2
  222. """ Calculate activations for every layer """
  223. activation = {}
  224. activation[0] = data
  225. for i in range(num_layers):
  226. activation[i+1] = sigmoid(numpy.dot(stack[i, "W"], activation[i]) + stack[i, "b"])
  227. """ Compute the groundtruth matrix """
  228. ground_truth = getGroundTruth(labels)
  229. """ Compute the class probabilities for each example """
  230. theta_x = numpy.dot(softmax_theta, activation[num_layers])
  231. hypothesis = numpy.exp(theta_x)
  232. probabilities = hypothesis / numpy.sum(hypothesis, axis = 0)
  233. """ Compute the traditional cost term """
  234. cost_examples = numpy.multiply(ground_truth, numpy.log(probabilities))
  235. traditional_cost = -(numpy.sum(cost_examples) / data.shape[1])
  236. """ Compute the weight decay term """
  237. theta_squared = numpy.multiply(softmax_theta, softmax_theta)
  238. weight_decay = 0.5 * lamda * numpy.sum(theta_squared)
  239. """ Add both terms to get the cost """
  240. cost = traditional_cost + weight_decay
  241. """ Compute Softmax 'theta' gradient """
  242. softmax_theta_grad = -numpy.dot(ground_truth - probabilities, numpy.transpose(activation[num_layers]))
  243. softmax_theta_grad = softmax_theta_grad / data.shape[1] + lamda * softmax_theta
  244. """ Compute intermediate difference values using Backpropagation algorithm """
  245. delta = {}
  246. delta[num_layers] = -numpy.multiply(numpy.dot(numpy.transpose(softmax_theta), ground_truth - probabilities),
  247. numpy.multiply(activation[num_layers], 1 - activation[num_layers]))
  248. for i in range(num_layers-1):
  249. index = num_layers - i - 1
  250. delta[index] = numpy.multiply(numpy.dot(numpy.transpose(stack[index, "W"]), delta[index+1]),
  251. numpy.multiply(activation[index], 1 - activation[index]))
  252. """ Compute the partial derivatives, with respect to the layer parameters """
  253. stack_grad = {}
  254. for i in range(num_layers):
  255. index = num_layers - i - 1
  256. stack_grad[index, "W"] = numpy.dot(delta[index+1], numpy.transpose(activation[index])) / data.shape[1]
  257. stack_grad[index, "b"] = numpy.sum(delta[index+1], axis = 1) / data.shape[1]
  258. """ Concatenate the gradient values and return as 'theta' gradient """
  259. params_grad = stack2Params(stack_grad)
  260. theta_grad = numpy.concatenate((numpy.array(softmax_theta_grad).flatten(),
  261. numpy.array(params_grad).flatten()))
  262. return [cost, theta_grad]
  263. """ Returns predictions using the trained Stacked Autoencoder model """
  264. def stackedAutoencoderPredict(theta, net_config, data):
  265. """ Calculate limits for Softmax parameters """
  266. input_size = net_config[-2]
  267. num_classes = net_config[-1]
  268. limit0 = 0
  269. limit1 = num_classes * input_size
  270. """ Extract Softmax and layer parameters """
  271. softmax_theta = theta[limit0 : limit1].reshape(num_classes, input_size)
  272. stack = params2Stack(theta[limit1 :], net_config)
  273. num_layers = len(stack) / 2
  274. """ Calculate the activations of the final layer """
  275. activation = data
  276. for i in range(num_layers):
  277. activation = sigmoid(numpy.dot(stack[i, "W"], activation) + stack[i, "b"])
  278. """ Compute the class probabilities for each example """
  279. theta_x = numpy.dot(softmax_theta, activation)
  280. hypothesis = numpy.exp(theta_x)
  281. probabilities = hypothesis / numpy.sum(hypothesis, axis = 0)
  282. """ Give the predictions based on probability values """
  283. predictions = numpy.zeros((data.shape[1], 1))
  284. predictions[:, 0] = numpy.argmax(probabilities, axis = 0)
  285. return predictions
  286. """ Loads data, trains the Stacked Autoencoder model and predicts classes for test data """
  287. def executeStackedAutoencoder():
  288. """ Define the parameters of the first Autoencoder """
  289. visible_size = 784 # size of input vector
  290. hidden_size1 = 200 # size of hidden layer vector of first autoencoder
  291. hidden_size2 = 200 # size of hidden layer vector of second autoencoder
  292. rho = 0.1 # desired average activation of hidden units
  293. lamda = 0.003 # weight decay parameter
  294. beta = 3 # weight of sparsity penalty term
  295. max_iterations = 200 # number of optimization iterations
  296. num_classes = 10 # number of classes
  297. """ Load MNIST images for training and testing """
  298. train_data = loadMNISTImages('train-images.idx3-ubyte')
  299. train_labels = loadMNISTLabels('train-labels.idx1-ubyte')
  300. """ Initialize the first Autoencoder with the above parameters """
  301. encoder1 = SparseAutoencoder(visible_size, hidden_size1, rho, lamda, beta)
  302. """ Run the L-BFGS algorithm to get the optimal parameter values """
  303. opt_solution = scipy.optimize.minimize(encoder1.sparseAutoencoderCost, encoder1.theta,
  304. args = (train_data,), method = 'L-BFGS-B',
  305. jac = True, options = {'maxiter': max_iterations})
  306. sae1_opt_theta = opt_solution.x
  307. """ Get the features corresponding to first Autoencoder """
  308. sae1_features = feedForwardAutoencoder(sae1_opt_theta, hidden_size1, visible_size, train_data)
  309. """ Initialize the second Autoencoder with the above parameters """
  310. encoder2 = SparseAutoencoder(hidden_size1, hidden_size2, rho, lamda, beta)
  311. """ Run the L-BFGS algorithm to get the optimal parameter values """
  312. opt_solution = scipy.optimize.minimize(encoder2.sparseAutoencoderCost, encoder2.theta,
  313. args = (sae1_features,), method = 'L-BFGS-B',
  314. jac = True, options = {'maxiter': max_iterations})
  315. sae2_opt_theta = opt_solution.x
  316. """ Get the features corresponding to second Autoencoder """
  317. sae2_features = feedForwardAutoencoder(sae2_opt_theta, hidden_size2, hidden_size1, sae1_features)
  318. """ Initialize Softmax Regressor with the above parameters """
  319. regressor = SoftmaxRegression(hidden_size2, num_classes, lamda)
  320. """ Run the L-BFGS algorithm to get the optimal parameter values """
  321. opt_solution = scipy.optimize.minimize(regressor.softmaxCost, regressor.theta,
  322. args = (sae2_features, train_labels,), method = 'L-BFGS-B',
  323. jac = True, options = {'maxiter': max_iterations})
  324. softmax_opt_theta = opt_solution.x
  325. """ Create a stack of the Stacked Autoencoder parameters """
  326. stack = {}
  327. stack[0, "W"] = sae1_opt_theta[encoder1.limit0 : encoder1.limit1].reshape(hidden_size1, visible_size)
  328. stack[1, "W"] = sae2_opt_theta[encoder2.limit0 : encoder2.limit1].reshape(hidden_size2, hidden_size1)
  329. stack[0, "b"] = sae1_opt_theta[encoder1.limit2 : encoder1.limit3].reshape(hidden_size1, 1)
  330. stack[1, "b"] = sae2_opt_theta[encoder2.limit2 : encoder2.limit3].reshape(hidden_size2, 1)
  331. """ Create a vector of the Stacked Autoencoder parameters for optimization """
  332. stack_params = stack2Params(stack)
  333. stacked_ae_theta = numpy.concatenate((softmax_opt_theta.flatten(), stack_params.flatten()))
  334. """ Create a neural network configuration, with number of units in each layer """
  335. net_config = [visible_size, hidden_size1, hidden_size2, num_classes]
  336. """ Load MNIST test images and labels """
  337. test_data = loadMNISTImages('t10k-images.idx3-ubyte')
  338. test_labels = loadMNISTLabels('t10k-labels.idx1-ubyte')
  339. """ Get predictions after greedy training """
  340. predictions = stackedAutoencoderPredict(stacked_ae_theta, net_config, test_data)
  341. """ Print accuracy of the trained model """
  342. correct = test_labels[:, 0] == predictions[:, 0]
  343. print """Accuracy after greedy training :""", numpy.mean(correct)
  344. """ Finetune the greedily trained model """
  345. opt_solution = scipy.optimize.minimize(stackedAutoencoderCost, stacked_ae_theta,
  346. args = (net_config, lamda, train_data, train_labels,),
  347. method = 'L-BFGS-B', jac = True, options = {'maxiter': max_iterations})
  348. stacked_ae_opt_theta = opt_solution.x
  349. """ Get predictions after finetuning """
  350. predictions = stackedAutoencoderPredict(stacked_ae_opt_theta, net_config, test_data)
  351. """ Print accuracy of the trained model """
  352. correct = test_labels[:, 0] == predictions[:, 0]
  353. print """Accuracy after finetuning :""", numpy.mean(correct)
  354. executeStackedAutoencoder()