#Deep Learning 上一篇主要是讲了全链接神经网络,这里主要讲的就是深度学习网络的一些设计以及一些权值的设置。神经网络能够根据模型的层数,模型的复杂度和神经元的多少大体能够分红两类:Shallow Neural Network和Deep Neural Network。比较一下二者:git
Network Name | Time | complexity | theoretical |
---|---|---|---|
Shallow Neural Network | more efficient to train | simple | powerful enough |
Deep Nerual Network | need more time | sophisticated | more meaningful |
###Autoencoder 什么是权重?在神经网络的模型里面权重表明的就是特征转换(feature transform)。从编码方面来讲,权重也能够当作是一种编码方式,把一个数据编码成另外一种数据。神经网络是一层一层的进行的,因此若是是一个好的权重的话,是应该能够提取出数据的特征的,也就是information-preserving encoding,一种能够保留有用信息的编码。第i层提取了特征,传到了i+1层,i+1层在i层的基础上再提取一些更加复杂的特征,传到i+2层。最后每一次均可以提取出特征来,这样就能够最大限度的把数据的特征保留下来。上面讲到的手写数字识别是能够把一个数字划分红不少个笔画,反过来,也能够由特征转换回数字。这种可逆的转换叫作information-preserving,经过神经网络转换的特征最后是能够转换回原来的输入,这也正是pre-train要作到的。因此pre-train要知足的特征就是要求information-preserving。由于拿到了特征又能够用获得的特征获得原输入,证实获得的特征是能够表明整个数据的,没有遗漏。github
想要获得这样的效果,只须要创建一个三层的神经网络便可。 ![]()
这个神经网络通过权重$$W_{ij}^{1}$$获得的结果就是encode以后的数据,也就是经过feature transform提取特征的过程,通过$$W_{ji}^2$$的就是解码过程,要求输出层输出的数据要和原数据差很少接近。整个网络是$$d - d' - d$$的结构,重点就在重构,输入层到隐含层是特征转换,隐含层到输出层是重构。这种结构咱们叫autoencode,对应编码和解码,整个过程就是在逼近indentity function(恒等函数) ![]()
可是这样好像画蛇添足,既然输出都是差很少的为何要画蛇添足?对于监督式学习,隐藏层其实就是特征转换,乘上一些权值转换成对应特征的值。就好像手写数字识别获得每个数字的特征,包含了一些提取出有用的特征,能够获得一些数据具备表明性的信息。对于非监督的学习,也可使用autoencode来作density estimation,密度估计,若是$$g(x) ≈ x$$密度较大,说明能够提取到的特征不少,不然就是密度很小。也能够作outlier detection,异常值的检测,也就是孤立点,噪音。找出哪些是典型的资料,哪些不是。 ![]()
对于编码器,咱们更加关心的实际上是中间的隐藏层,也就是特征转换权重$$W_{ij}^{(1)}$$天然对应的error function:$$\sum_{i=1}^d(g_i(x) - x_i)^2$$ ![]()
三层的autoencode也叫basic autoencode 由于它是最简单的了。一般限定$$d' < d$$方便数字的编码,数据集能够表示为$$(x_1, y_1 = x_1) (x_2, y_2 = x_2)(x_3, y_3 = x_3)......$$因此能够当作是一个非监督式的学习,一个重要的限制条件是$$W_{ij}^{(1)} = W_{ji}^{(2)}$$ 解码权重等于编码权重,这起到了regularization的做用。 ![]()
深度学习中,basic autoencoder的过程也就对应着pre-training的过程,使用这种方法,对无label的原始数据进行编码和解码,获得的编码权重$$W_{ij}^{(1)}$$就能够做为pre-trained的比较不错的初始化权重,也就是做为深度学习中层与层之间的初始化权重。 ![]()
因此,pre-train的训练过程:首先,autoencoder会对深度学习网络第一层(即原始输入)进行编码和解码,获得编码权重$$W_{ij}^{(1)}$$,做为网络第一层到第二层的的初始化权重;而后再对网络第二层进行编码和解码,获得编码权重W(1)ij,做为网络第二层到第三层的初始化权重,以此类推,直到深度学习网络中全部层与层之间都获得初始化权重。值得注意的是,对于l-1层的网络$${{x_n^{l-1}}}$$,autoencoder中的d˘应与下一层(即l层)的神经元个数相同。 ![]()
这里介绍的只是一种最简单的,还有不少的编码器,稀疏编码器,去燥编码器等等。 ####②regularization 控制模型的复杂度就是使用正则化了。 ![]()
神经网络每一层都有不少的权重和神经元,这就致使了模型的复杂度会很大,regularization是必要的。 咱们以前介绍过一些方法: 1.structural decisions。架构能够设计的简单点,可是深度学习神经网络的结构是不可能简单的,这辈子的不可能。只能是相对来讲用dropout减轻一些压力。2.weight decay or weight elimination regularizers。可使用正则化来减少权值。 3.early stop。不要训练的这么准确,差很少差很少就够了。 ![]()
下面是一种新的regularization的方式: ![]()
###Denoising Autoencoder 回顾一下以前的overfit的缘由: 算法
和样本数量,噪声大小都有关系,若是数据量是不变的,那么noise的影响就会很是大。那么这个时候实现regularization的一个方法就是消除noise的影响。 有一种方法是对数据clean操做,可是这样费时费力。有一种更加牛逼的作法,就是在数据中添加一些noise再训练。 ![]()
这种作法的初衷是想创建一个比较健壮,容错性高的autoencode。在一个已经训练好的autoencode中,g(x) ≈ x的,对于已经容错性很高,比较健壮的autoencode,获得的输出是和x很接近的。好比手写数字识别,一个很规范很正的6输进去能够获得一个6,可是若是原始的图片是歪歪的6,而后仍是能够获得6的话那么这个自编码器就是一个健壮的自编码器了。比较不是每个人写6都是写的正正,这就使得这个自编码器的抗噪能力很强。 因此,最后咱们要训练的样本集$$(x_1',y_1 = x_1),(x_2',y_2 = x_2),(x_3',y_3 = x_3)......$$ 其中$$x_n' = x_n + noise$$ 因此使用这种autoencode的目的就是使得加入了有噪音的x均可以获得纯净的x。不只能从纯净的样本中编解码获得纯净的样本,还能从混入noise的样本中编解码获得纯净的样本。这样获得的权重初始值更好,由于它具备更好的抗噪声能力,即健壮性好。实际应用中,denoising autoencoder很是有用,在训练过程当中,输入混入人工noise,输出纯净信号,让模型自己具备抗噪声的效果,让模型健壮性更强,最关键的是起到了regularization的做用。这也是以前说的去燥自编码器。 ![]()
#linear autoencode 刚刚介绍的自编码器是非线性的,由于中间的tanh(x)函数就是非线性的。常见的autoencode经常使用于在深度学习中,这里要介绍的是线性的自编码器,linear autoencode。对于一个线性的自编码器,只须要不包括非线性转换就行了。 因此,就变成$$h_k(x) = \sum_{j = 0}^{d'}W_{jk}^{(2)}\sum_{i = 0}^{d}W_{ij}^{(1)}$$ 这里有三个限制条件: ①移除bias项,保持维度一致。 ②编码权重与解码权重一致:$$W_{ij}^{(1)} = W_{jk}^{(2)} = W_{ij}$$ ③$$d' < d$$ 因为两个权重是同样的,W的维度是dxd',x的维度是dx1,因此整个公式能够变为:$$h_k(x) = WW^Tx$$ 可是要求$$h(x) = x$$ 因此error function$$E_{in}(h) = E_{in}(W) = \frac{1}{N}\sum_{n=1}^{N}|x_n - WW^Tx_n|^2 with(dxd')W$$ 对于那个协方差矩阵首先能够进行特征值分解$$WW^T = VΓV^T$$其中Γ是一个对角矩阵,V矩阵知足$$VV^T = I_d$$ 而I矩阵有一个很重要的性质,因为W是dxd'的,d < d’。有I的非零元素的数量是不大于d'的。 ![]()
###证实 对于一个矩阵W(dxd',d < d'),那么有Rank(W) <= d'。这个结论直接给出,不用证实。假设有两个矩阵A,B,C = AB,那么AX = C的解就是B,也就是惟一解,也就是说Rank(A, C) = Rank(A )。证实:对于Rank(A,C)和Rank(A)无非就是两种状况,=和>。<是没有的,Rank(A)是不可能大于Rank(A,C)的。若是是Rank(A) < Rank(A,C),那么这个增广矩阵(A,C)变换成初等矩阵以后,最后一行就会出现$$[0,0,0,0,a]$$的状况,0 != a,天然就是无解了。因此证实了上诉状况必定是有Rank(A) = Rank(A,C) 而因为R(C) <= R(A,C),因此R(C) <= R(A),同理,也能够证实得R(C) <= R(B),而在这里$$A = W,B = W^T,C = WW^T$$因此,$$rank(WW^T)$$是不大于d'的,由于一个矩阵的秩是不能够大于它最小的维度的。$$Rand(W) = Rank(W^T) <= d'$$综上所诉,$$Rank(WW^T) <= d'$$更重要的是,秩就是这个矩阵特征值的数量,而I矩阵的斜对角线就是WW^T矩阵的特征值,因此才有I的非零元素的数量是不大于d'的。bash
回到正文。$$VV^T = I => VIV^T = I $$那么就能够推出$$x_n = VIV^Tx_n$$ 网络
因此,通常的,若是咱们有M个N维向量,想将其变换为由R个N维向量表示的新空间中,那么首先将R个基按行组成矩阵A,而后将向量按列组成矩阵B,那么两矩阵的乘积AB就是变换结果,其中AB的第m列为A中第m列变换后的结果。 架构
问题来了,这5个数据都是2维度的,如今想降到一维,若是咱们必须使用一维来表示这些数据,又但愿尽可能保留原始的信息,你要如何选择?这个问题其实是要在二维平面中选择一个方向,将全部数据都投影到这个方向所在直线上,用投影值表示原始记录。这是一个实际的二维降到一维的问题。 一种直观的见解是:但愿投影后的投影值尽量分散。同一个维度数据之间不"挤"在一块儿,由于挤在一块儿表达不了数据自己了。而表征数据之间离散程度,就考虑到了方差。具体作法就是 想办法让高维数据左乘一个矩阵,获得降维后的数据,降维后的数据的方差最大化。在线性代数中学过,一个矩阵能够当作是不少个列向量,每个向量表明着一个物体的数据。要表达这个矩阵真的须要这么多数据吗,显然不必定,求解一个矩阵的秩R,看看R 的值,在绝大多数状况下,矩阵都不是满秩的,说明矩阵中的元素表达客观世界中的物体有一些是多余的。 既然是求离散程度,那么就是方差了,对于每个字段的方差$$Var(a) = \frac{1}{m}\sum_{i=1}^{m}(a_i - u)^2$$若是已经去除了均值那就能够直接转换$$Var(a) = \frac{1}{m}\sum_{i=1}^{m}(a_i)^2$$ 对于上面二维降成一维的问题来讲,找到那个使得方差最大的方向就能够了。不过对于更高维,还有一个问题须要解决。考虑三维降到二维问题。与以前相同,首先咱们但愿找到一个方向使得投影后方差最大,这样就完成了第一个方向的选择,继而咱们选择第二个投影方向。可是若是第二次仍是坚持选择方差最大的方向,那么确定仍是会和第一个方向相同的,这样表达的信息颇有可能重合,因此应该要加上一些限制。从直观上说,让两个字段尽量表示更多的原始信息,咱们是不但愿它们之间存在(线性)相关性的,由于相关性意味着两个字段不是彻底独立,必然存在重复表示的信息。 事实上两个字段的相关性是能够用协方差来表示。$$Cov(a,b) = \frac{1}{m}\sum_{i=1}^{m}a_ib_i$$协方差为0的时候,两个字段是互相独立的。协方差为0因此只能在一个基的正交方向选择第二个基。将一组N维向量降为K维(K大于0,小于N),其目标是选择K个单位(模为1)正交基,使得原始数据变换到这组基上后,各字段两两间协方差为0,而字段的方差则尽量大(在正交的约束下,取最大的K个方差)。 ####⑤协方差矩阵的优化 协方差矩阵的对角线表明了原来样本在各个维度上的方差,其余元素表明了各个维度之间的相关关系。因此咱们须要把协方差对角化,由于咱们须要处理的就是维度中的关系,而不是维度之间的。设Y=PX,则Y为X对P作基变换后的数据。设Y的协方差矩阵为D,则有: dom
总结:一开始对数据进行降维操做,咱们想到的就是要使得数据投影以后方差最大,那么就须要寻找一组基使达成这个条件。可是发现若是只是找最大的方差是会有重复,好比第一个发现的基是最大的,那么第二个方向确定也差很少是这个方向,这样信息就有重复,因此又至关数据维度之间是应该有非相关性的,因而就使用到了协方差来表明,可是咱们须要的是数据里的非相关性最大化,数据间的最大化方差最大已经处理了,因此这个时候协方差只须要关注对角线,天然就是对角化了。又发现对角化其实就是特征分解的一个过程,最后求出结果。 另外一个方面,W咱们能够看作是基的组合,WX就是降维以后的数据$$|x - ww^Tx|^2$$就是要求了降维以后的数据要和原来的数据相差不远,为何不是X-WX呢?首先这两个东西不是同纬度的,不可能计算,其次乘上一个转置其实就是转换回来的结果了。若是压缩以后转换回来的结果和原来差很少,那不就证实了这个降维是OK的吗?因此这就和Autoencode的思想同样了,因此上面linear autoencode的过程也能够看作就是PCA的公式化推导。 ####⑤PCA算法过程: ①将原始数据按列组成n行m列矩阵X ②均值化操做。 ③求协方差 ④求特征值特征向量 ⑤排列取前k大的特征值对应的特征向量 ⑥降维操做 #Coding ####Autoencode 编码器有几个很重要的特征: ①编码器是数据相关的,这就意味着只能压缩那些和训练数据相关的数据,好比用数字的图像训练了而后有跑去压缩人脸的图片,这样效果是不好的。 ②编码器不管是训练多少次,都是有损的。 ③自动编码器是从数据样本中自动学习的,这意味着很容易对指定类的输入训练出一种特定的编码器,而不须要完成任何新工做 ####Tool 先介绍一下工具类:机器学习
import numpy as np
from keras.datasets import mnist
import matplotlib.pyplot as plt
def get_dataSets():
(x_train, _), (x_test, _) = mnist.load_data()
x_train = x_train.astype('float32')/255.
x_test = x_test.astype('float32')/255.
x_train = x_train.reshape(len(x_train), np.prod(x_train.shape[1:]))
x_test = x_test.reshape(len(x_test), np.prod(x_test.shape[1:]))
return x_train, x_test
pass
复制代码
首先是引入数据,mnist.load_data()这个数据导入有点问题,上网百度了一下发现百分之90的博客都是有问题本身下的,因此不例外我也是本身下载了4个压缩包放在了当前目录data下面。 获得数据时候先进行归一化操做,而后reshape,由于后面一开始用到的是单层的编码器,用的全链接神经网络,因此应该变成是二维的数据。函数
def show(orignal, x):
n = 10 # how many digits we will display
plt.figure(figsize=(20, 4))
for i in range(n):
ax = plt.subplot(2, n, i + 1)
plt.imshow(orignal[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax = plt.subplot(2, n, i + 1 + n)
plt.imshow(x[i].reshape(28, 28))
plt.gray()
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
plt.show()
复制代码
这个函数用于显示,传入的参数是原始图像和预测的图像。好像都蛮直观的。 虽然每一种编码器都是用了一个类来封装,可是每个只有应该create方法,返回一个已经训练好的autoencode,因此下面全部的代码都是在create里面。 ####①单层自编码器 单层自编码器就是以前说的最简单的base autoencode。使用Keras实现。 所有都封装在一个类里面,用类的create函数返回一个已经训练好的autoencode。使用的数据集是mnist数据集。工具
def create(self):
x_train, x_test = get_dataSets()
encoding_dim = 32
input_img = Input(shape=(784,))
复制代码
获得数据,隐藏层神经元的数量,入口函数。
encoded = Dense(encoding_dim, activation='relu')(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)
复制代码
编码层,最后那个小括号里面(input_img)就是输入的内容了,解码层就是从encoded输入。Dense里面定义的是当前层的神经元,decoded层输出了,天然就是784 = 28 x 28个了。
autoencoder = Model(input=input_img, output=decoded)
encoder = Model(input=input_img, output=encoded)
encoded_input = Input(shape=(encoding_dim,))
decoder_layer = autoencoder.layers[-1]
decoder = Model(input=encoded_input, output=decoder_layer(encoded_input))
复制代码
接下来的这几个就是获得完整的模型,使用Keras的Model API来构建。下面的几个就是encoder获得编码层,获得解码层。都很直观。
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.fit(x_train, x_train,
epochs=50,
batch_size=128,
shuffle=True,
validation_data=(x_test, x_test))
return autoencoder, encoder, decoder
复制代码
以后就是模型的编译,模型的训练了。 最后的效果:
input_img = Input(shape=(784,))
encoded = Dense(128, activation='relu')(input_img)
encoded = Dense(64, activation='relu')(encoded)
encoded = Dense(32, activation='relu')(encoded)
decoded = Dense(64, activation='relu')(encoded)
decoded = Dense(128, activation='relu')(decoded)
decoded = Dense(784, activation='sigmoid')(decoded)
autoencoder = Model(input=input_img, output=decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.fit(x_train, x_train,
epochs=1,
batch_size=128,
shuffle=True,
validation_data=(x_test, x_test))
复制代码
中间那几层获得编码层的那些去掉了。最后看看效果。
encoded = Dense(encoding_dim, activation='relu',
activity_regularizer=regularizers.l1(10e-5))(input_img)
decoded = Dense(784, activation='sigmoid')(encoded)
复制代码
####④卷积自编码器 其实就是卷积神经网络作编码而已。
x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
x_test = np.reshape(x_test, (len(x_test),28, 28, 1))
复制代码
数据格式要正确,由于是卷积处理了,要按照(个数,长,宽,颜色)排列。
input_img = Input(shape=(28, 28, 1))
x = Convolution2D(16, (3, 3), activation='relu', border_mode='same')(input_img)
x = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(8, (3, 3), activation='relu', border_mode='same')(x)
x = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(8, (3, 3), activation='relu', border_mode='same')(x)
encoded = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(8, (3, 3), activation='relu', border_mode='same')(encoded)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(8, (3, 3), activation='relu', border_mode='same')(x)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(16, 3, 3, activation='relu')(x)
x = UpSampling2D((2, 2))(x)
decoded = Convolution2D(1, (3, 3), activation='sigmoid', border_mode='same')(x)
复制代码
encode和decode是两个对应的东西,反着来而已,因为是一个图片一个图片的进来,因此输出就是一个。
autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.fit(x_train, x_train, epochs=20, batch_size=256,
shuffle=True,
validation_data=(x_test, x_test))
return autoencoder
复制代码
最后就是常规操做了。 这个训练时间有点长,电脑不行没办法。效果:
def get_nosing(noise_factor):
(x_train, _), (x_test, _) = mnist.load_data()
x_train = x_train.astype('float32') / 255.
x_test = x_test.astype('float32') / 255.
x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
x_test = np.reshape(x_test, (len(x_test), 28, 28, 1))
noise_factor = noise_factor
x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape)
x_test_noisy = x_test + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape)
x_train_noisy = np.clip(x_train_noisy, 0., 1.)
x_test_noisy = np.clip(x_test_noisy, 0., 1.)
return x_train, x_train_noisy, x_test, x_test_noisy
pass
复制代码
时间就是使用高斯分布来获得了。
x_train, x_train_noisy, x_test, x_test_noisy = get_nosing(0.5)
input_img = Input(shape=(28, 28, 1))
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(input_img)
x = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(x)
encoded = MaxPooling2D((2, 2), border_mode='same')(x)
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(encoded)
x = UpSampling2D((2, 2))(x)
x = Convolution2D(32, 3, 3, activation='relu', border_mode='same')(x)
x = UpSampling2D((2, 2))(x)
decoded = Convolution2D(1, 3, 3, activation='sigmoid', border_mode='same')(x)
autoencoder = Model(input_img, decoded)
autoencoder.compile(optimizer='adadelta', loss='binary_crossentropy')
autoencoder.fit(x_train_noisy, x_train, shuffle=True,epochs=1, batch_size=128,
validation_data=(x_test_noisy, x_test))
return autoencoder
复制代码
这里的网络有些许不一样,是由于便于计算罢了。过程是同样,扔的数据不一样而已。效果:
####PCA PCA就不用mnist数据集了,毕竟784维降到2维不是很好看,使用iris数据集。 获取数据那些就不写了,毕竟蛮简单的。
class pca(object):
def fit(self, data_features, y):
data_mean = np.mean(data_features, axis=0)
data_features -= data_mean
cov = np.dot(data_features.T, data_features)
eig_vals, eig_vecs = np.linalg.eig(cov)
eig_pairs = [(np.abs(eig_vals[i]), eig_vecs[:, i]) for i in range(len(eig_vals))]
a = np.matrix(eig_pairs[0][1]).T
b = np.matrix(eig_pairs[1][1]).T
u = np.hstack((a, b))
data_new = np.dot(data_features, u)
return data_new
def show(self, data_new):
plt.scatter(data_new[:, 0].tolist(), data_new[:, 1].tolist(), c='red')
plt.show()
pass
复制代码
步骤其实很简单,均值化求特征值排序组合向量基对原数据作乘法。
####最后附上GitHub代码:github.com/GreenArrow2…