本证主要相对深度学习的一些学习技巧做一些概括。
对图像数据进行扩展的常用方法包括:对图像进行角度偏移,左右偏移,上下偏移,随机放大或则缩小,水平翻转。
keras 数据预处理:
# -*- coding: utf-8 -*-
from keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img,array_to_img
import matplotlib.pyplot as plt
import numpy as np
data_generate = ImageDataGenerator(
rotation_range=20, #控制旋转角度
width_shift_range=0.2, #左右偏移度
height_shift_range=0.2, #上下偏移度
shear_range=0.2, #剪切变换程度
zoom_range=0.2, #放大缩小倍数
horizontal_flip=True,
fill_mode='nearest'
)
img = load_img(r'C:\Users\lenovo\Desktop\cat.1500.jpg')
x = img_to_array(img) #
x = x.reshape((1,)+x.shape)
i = 0
fig, axes = plt.subplots(4, 5,sharex=True, sharey=True)
for batch in data_generate.flow(x, batch_size=1, save_prefix='cat', save_format='jpeg'):
img = batch.reshape(500, 490, 3).astype(np.int)
axes[i//5, i%5].imshow(img)
i+=1
if i >= 20:
break
plt.subplots_adjust(wspace=0,hspace=0)
plt.show()
原始图像:
这里我们直接使用变换函数,置于是怎么实现的这种变化我们暂且不谈(埋下疑惑的种子)。
分析:通过这种变换之后使得神经网路对于多种形态的同一物体也能够很好的识别出来。
0均值实现:
X = np.random.rand(2, 5)
X -= np.mean(X, axis=0)
每个样本值减去样本的平局值。
图像0均值操作:
import numpy as np
from keras.preprocessing.image import load_img,img_to_array,array_to_img
import matplotlib.pyplot as plt
X = load_img(r'C:\Users\lenovo\Desktop\cat.1500.jpg')
X = img_to_array(X) #shape(500, 490, 3)
X[:, :, 0] -= np.mean(X[:, :, 0]) #第一个通道
X[:, :, 1] -= np.mean(X[:, :, 1]) #第二个通道
X[:, :, 2] -= np.mean(X[:, :, 2]) #第三个通道
X = X.astype(np.int) #修改数据类型
plt.imshow(X)
plt.show()
两种方法:
方法一:
X = np.random.rand(2, 5)
X -= np.mean(X, axis=0)
X /= np.std(X, axis=0)
print(X)
[ 1. -1. -1. 1. -1.]
[-1. 1. 1. -1. 1.]]
方法二:
X_normed = X/=X.max(axis=0)#归一化为(0,1)之间
主成分分析之前了解的比较多这里不加赘述。
import numpy as np
X = np.random.rand(2, 5)
X -= np.mean(X, axis=0)
Cov = np.dot(X.T, X) / X.shape[0]
U, S, V = np.linalg.svd(Cov)
Xrot = np.dot(X, U) #数据取相关性
PCA = np.dot(X, U[:, :100]) #获取前100列数据
print(X, Xrot)
(Whitening)的目的是降低数据的冗余性,数据经过白化以后应该具有以下性质
在主成分分析算法中我们已经消除了数据之间的相关性,白化还需要使得数据具有相同的方差。
数据白化实现:
XWhiten = PCA/np.sqrt(S + 1e-6) #除以特征值,也就是奇异值的平方根
使用随机高斯分布产生初始化网络参数
import numpy as np
import matplotlib.pyplot as plt
def neurous(sqrt=False, num=1000, b=0):
'''神经元模型'''
x = np.ones(num)
if not sqrt:
w = np.random.rand(num)
else:
w = np.random.rand(num)/np.sqrt(num)
return np.dot(w, x) + b
#迭代100000次获得不同随机生成的权重参数得到的加权输出
z1 = []
#迭代100000次获得不同的随机生成的权重参数得到的加权输出
for i in range(100000):
z1.append(neurous())
#显示100000次加权输出的直方图
n, bins, patches = plt.hist(z1, 100)
plt.title('Histigram of z = wx + b')
plt.show()
计算激活值:
sigmoid = lambda x:1./(1.+np.exp(-x))
a1 = sigmoid(np.array(z1))
当使用sigmoid函数作为我们的激活函数时,激活值集中在0,1的附近使得神经元基本处于饱和状态。为什么会出现这种情况呢。下面我们来分析:
sigmoid函数曲线:
由图中观察0,1附近的曲线趋于平缓,可想而知经过一次迭代之后该值的改变时十分微小的,输出神经元接近于饱和,也就是下一层的神经元输入饱和,后面通过反向传播BP算法对权重进行微调所带来的变化也不会太明显,这就形成了一个“恶性循环” 最终网络训练速度变得非常的慢,而且更容易过拟合。
如下我选择了一个input,h_layer,output,[1,1,1]的网络。
L层损失为: δ L = ( y − a 3 ) ⊙ f ‘ ( z 3 ) \delta_L = (y-a_3)\odot f^`(z_3) δL=(y−a3)⊙f‘(z3)
对w_2求偏导数: ∂ L ∂ w 2 = δ L . a 2 \frac{\partial L}{\partial w_2} = \delta_L . a_2 ∂w2∂L=δL.a2
w 2 = w 2 − d w 2 w_2 = w_2 - dw_2 w2=w2−dw2
a 2 在 0 附 近 时 , w 2 的 导 数 是 十 分 为 微 小 的 , 所 以 L 层 的 误 差 基 本 上 是 不 变 的 , 所 以 反 过 来 在 求 w 2 时 , 这 种 变 化 还 是 小 的 a_2在0附近时,w_2的导数是十分为微小的,所以L层的误差基本上是不变的,所以反过来在求w_2时,这种变化还是小的 a2在0附近时,w2的导数是十分为微小的,所以L层的误差基本上是不变的,所以反过来在求w2时,这种变化还是小的
产生上面结果的原因是随机产生权重参数的标准差太大,下面该进方法是使用均值为0,标准差为 1 / n 1 / \sqrt{n} 1/n 高斯分布的效果。
该方法有效的解决了神经元的过饱和问题,即保证了网络模型参数在初始化阶段都符合高斯分布的输入,也提高了算法的收敛速度
z1 = []
for i in range(100000):
# w= np.random.rand(num)/sqrt(num)
z1.append(neurous(sqrt=True))
当然在参数初始化这一块我门可以使用已经训练好的参数,这些参数是经过一定训练的所以他的均值和方差都是我们想要的,而且能够加快我们学习速度,这种方式在深度学习中被称为‘迁移学习’。
如果我们在训练过程中需要将程序停止,这时候我们就可以将采纳数保存,等到下一次我们再次训练的时候再将这些参数拿出来,继续训练。
关于正则方法我在这篇博客中介绍了L2正则化,并且是在实例应用中,比较详细了解具体的实现细节,并且我在后面给出了具体的求导公式的过程。博客
def dropout(x, level):
# level是概率,必须在0~1之间
if level < 0. or level >= 1.:
raise Exception('Dropout level must be in [0, 1]')
retain_prob = 1. - level
sample = np.random.binomial(n=1, p=retain_prob, size=x.shape)
x *= sample # 0, 1与x相乘屏蔽部分神经元shape(500, 490, 3)
x /= retain_prob
return x