数据预处理(代码实现)

前言

本证主要相对深度学习的一些学习技巧做一些概括。

图像数据

对图像数据进行扩展的常用方法包括:对图像进行角度偏移,左右偏移,上下偏移,随机放大或则缩小,水平翻转。
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()

原始图像:

数据预处理(代码实现)_第1张图片
输出图像结果:

数据预处理(代码实现)_第2张图片
这里我们直接使用变换函数,置于是怎么实现的这种变化我们暂且不谈(埋下疑惑的种子)。
分析:通过这种变换之后使得神经网路对于多种形态的同一物体也能够很好的识别出来。

四种预处理常用方法

  • 0均值 (Zero Centralization)
  • 归一化 (Normalization)
  • 主成分分析 (Princiapl Component Analysis, PCA)
  • 白化 (Whitening)

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()

新图片:
数据预处理(代码实现)_第3张图片

归一化

两种方法:

  • 将0均值化后的数据的每一维除以每一维的标准差
  • 另一种方法是将数据中的每一维归一化到区间[a,b]。第二种归一化方法只适用于数据的不同维度应该具有相同的重要性时,也就是每一维度数据的权重一样时。

方法一:

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()

效果图如下:
数据预处理(代码实现)_第4张图片

计算激活值:

sigmoid = lambda x:1./(1.+np.exp(-x))
a1 = sigmoid(np.array(z1))

输出结果图像:
数据预处理(代码实现)_第5张图片

当使用sigmoid函数作为我们的激活函数时,激活值集中在0,1的附近使得神经元基本处于饱和状态。为什么会出现这种情况呢。下面我们来分析:
sigmoid函数曲线:
数据预处理(代码实现)_第6张图片

由图中观察0,1附近的曲线趋于平缓,可想而知经过一次迭代之后该值的改变时十分微小的,输出神经元接近于饱和,也就是下一层的神经元输入饱和,后面通过反向传播BP算法对权重进行微调所带来的变化也不会太明显,这就形成了一个“恶性循环” 最终网络训练速度变得非常的慢,而且更容易过拟合。
如下我选择了一个input,h_layer,output,[1,1,1]的网络。
数据预处理(代码实现)_第7张图片

L层损失为: δ L = ( y − a 3 ) ⊙ f ‘ ( z 3 ) \delta_L = (y-a_3)\odot f^`(z_3) δL=(ya3)f(z3)
对w_2求偏导数: ∂ L ∂ w 2 = δ L . a 2 \frac{\partial L}{\partial w_2} = \delta_L . a_2 w2L=δL.a2
w 2 = w 2 − d w 2 w_2 = w_2 - dw_2 w2=w2dw2
a 2 在 0 附 近 时 , w 2 的 导 数 是 十 分 为 微 小 的 , 所 以 L 层 的 误 差 基 本 上 是 不 变 的 , 所 以 反 过 来 在 求 w 2 时 , 这 种 变 化 还 是 小 的 a_2在0附近时,w_2的导数是十分为微小的,所以L层的误差基本上是不变的,所以反过来在求w_2时,这种变化还是小的 a20w2Lw2

产生上面结果的原因是随机产生权重参数的标准差太大,下面该进方法是使用均值为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))

加权值输出为:
数据预处理(代码实现)_第8张图片

激活值输出为:
数据预处理(代码实现)_第9张图片

当然在参数初始化这一块我门可以使用已经训练好的参数,这些参数是经过一定训练的所以他的均值和方差都是我们想要的,而且能够加快我们学习速度,这种方式在深度学习中被称为‘迁移学习’。
如果我们在训练过程中需要将程序停止,这时候我们就可以将采纳数保存,等到下一次我们再次训练的时候再将这些参数拿出来,继续训练。

正则化方法

关于正则方法我在这篇博客中介绍了L2正则化,并且是在实例应用中,比较详细了解具体的实现细节,并且我在后面给出了具体的求导公式的过程。博客

神经网络dropout层实现

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
  • x: [500, 490, 3]
  • retain_prob:为保留参数概率
    np.random.binomial(n=1, p=retain_prob, size=x.shape)
    函数公式为:
    P ( X = k ) = C n k p k ( 1 − p ) n − k P(X=k)=C_n^kp^k(1-p)^{n-k} P(X=k)=Cnkpk(1p)nk
    是一个二项分布函数,函数返回值为retain_prob在size次抽样中实现的次数。
    所以就上述例子我们会得到一个形状和图形一样的只包含(0,1)的数三维数组,这样和数据进行点乘的话我们就可以让结点的数据为0从而让该神经元对应的权重全部失效。
    Dropout层可以用于防止过度拟合,当网络规模很小时,加入Dropout层的预测结果反而变差,在大型网络中加入Dropout层会增加网络的训练时间,对于大型网络是否要加入Dropout层要根据实际的效果进行对比

训练过程技巧

  • 1、精确曲线和损失曲线。通过观察曲线的变化来判断是否发生过拟合。
  • 网络微调fine-tune,
    • 节省时间
    • 当新的数据集很小时,直接训练容易造成过拟合,网络微调能够避免发生这种情况。
    • 当网络模型修改预测分类时(从1000个分类 改为 20个分类),并不需要重新训练该神经网络模型,只需要在修改完网络在输出层参数后微调最后几层全连接层,即可保持在之前的分类准确率的情况下得到新的模型文件。

自我总结

  • 关于数据分布这一部分自己并不是太熟悉,需要补一下(方差,偏差)

你可能感兴趣的:(深度学习原理与实践)