深度学习基础

深度学习

一、神经网络基础

1.感知机

双输入感知机


带偏置的感知机

w1 和 w2是控制输入信号的重要性的参数,而偏置是调整神经元被激活的容易程度(输出信号为 1 的程度)的参数。
单层感知机的局限性:只能表示由一条直线分割的空间。
多层感知机

# 与门
def AND(x1, x2):
    x = np.array([x1, x2])
    w = np.array([0.5, 0.5])
    b = -0.7
    tmp = np.sum(w * x) + b
    if tmp <= 0:
        return 0
    else:
        return 1

# 与非门
def NAND(x1, x2):
    x = np.array([x1, x2])
    w = np.array([-0.5, -0.5]) # 仅权重和偏置与AND不同!
    b = 0.7
    a = np.sum(w * x) + b
    if a <= 0:
        return 0
    else:
        return 1

# 或门
def OR(x1, x2):
    x = np.array([x1, x2])
    w = np.array([0.5, 0.5]) # 仅权重和偏置与AND不同!
    b = -0.2
    a = np.sum(w * x) + b
    if a <= 0:
        return 0
    else:
        return 1

# 异或门(多层感知机)    
def XOR(x1, x2):
    s1 = NAND(x1, x2)
    s2 = OR(x1, x2)
    y = AND(s1, s2)
    return y

2.神经网络

神经网络

带激活函数的神经元

激活函数

①sigmoid 函数

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')
x = np.linspace(-5, 5, 100)
y = 1 / (1 + np.exp(-x))
plt.plot(x, y)
plt.axhline(y=0, color='k')
plt.axvline(x=0, color='k')
plt.show()

②tanh函数(双曲正切函数)


③ReLU 函数(修正线性单元)


%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
plt.style.use('seaborn-whitegrid')
x = np.linspace(-5, 5, 100)
y = np.maximum(0, x)
plt.plot(x, y)
# plt.axhline(y=0, color='k')
# plt.axvline(x=0, color='k')
plt.show()

④Leaky ReLU(带泄露的修正线性单元)


⑤PReLU(Parameteric Rectified Linear Unit,参数化修正线性单元)


α=0.7

⑥RReLU(Randomized Leaky Rectified Linear Unit,随机带泄露的修正线性单元)

α值随机

⑦ELU(Exponential Linear Unit,指数修正线性单元)


α=0.7

更多激活函数见:https://www.cnblogs.com/CJT-blog/p/10421822.html

3.矩阵乘法

np.dot(X, W)

4.神经网络前向传播算法

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def identity_function(x):
    return x

X = np.array([1.0, 0.5])

W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
A1 = np.dot(X, W1) + B1
Z1 = sigmoid(A1)

W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)

W3 = np.array([[0.1, 0.3], [0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3)
  • 模块化
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def identity_function(x):
    return x

def init_network():
    network = {}
    network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
    network['b1'] = np.array([0.1, 0.2, 0.3])
    network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
    network['b2'] = np.array([0.1, 0.2])
    network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]])
    network['b3'] = np.array([0.1, 0.2])
    return network

def forward(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']
    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = identity_function(a3)
    return y

network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)

5.恒等函数与softmax函数

(1)恒等函数

恒等函数会将输入按原样输出,对于输入的信息,不加以任何改动地直接输出。

def identity_function(x):
    return x

恒等函数一般用于回归问题。

(2)softmax函数


softmax函数返回的是每个类别的概率。

def softmax(a):
    exp_a = np.exp(a)
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y

缺陷:exp()为指数函数,exp(a)可能为一个很大的值,导致溢出(inf)。

  • 溢出缺陷改进


def softmax(a):
    c = np.max(a)
    exp_a = np.exp(a - c)    # 溢出对策
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a
    return y

softmax函数一般作为输出层的激活函数。

二、神经网络学习算法

1.损失函数(loss function)

(1)均方误差(mean squared error,MSE)


yk 是表示神经网络的输出,tk 表示监督数据,k 表示数据的维数。

def mean_squared_error(y, t):
    return 0.5 * np.sum((y-t)**2)

(2)交叉熵(cross entropy error)


log 表示以 e为底数的自然对数ln(loge)。yk 是神经网络的输出,tk 是正确解标签。并且,tk 中只有正确解标签的索引为 1,其他均为 0(one-hot 表示)。

def cross_entropy_error(y, t):
    delta = 1e-7
    return -np.sum(t * np.log(y + delta))

2. mini-batch学习

mini-batch给训练过程引入了随机性,使得训练过程不致陷入局部最优。


  • 独热编码
def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    batch_size = y.shape[0]
    return -np.sum(t * np.log(y + 1e-7)) / batch_size
  • 非独热编码
def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size

3.梯度下降法

  • 求导数


def numerical_diff(f, x):
    h = 1e-4 # 0.0001
    return (f(x+h) - f(x-h)) / (2*h)
  • 求梯度
    函数z=f(x,y)在点P(x,y)的梯度,记作gradf(x,y)或▽f(x,y)。gradf(x,y)=


def numerical_gradient(f, x):
    h = 1e-4    # 0.0001
    
    # 生成和x形状相同的数组
    grad = np.zeros_like(x)
    
    for idx in range(x.size):
        tmp_val = x[idx]
        
        # f(x+h)的计算
        x[idx] = tmp_val + h
        fxh1 = f(x)
        
        # f(x-h)的计算
        x[idx] = tmp_val - h
        fxh2 = f(x)
        
        grad[idx] = (fxh1 - fxh2) / (2*h)
        # 还原值
        x[idx] = tmp_val 
    return grad
  • 梯度下降算法
def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x
    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x -= lr * grad
    return x
  • 梯度下降法可视化
def function_2(x):
    return x[0]**2 + x[1]**2

def draw_gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x
    x_history = []
    for i in range(step_num):
        x_history.append(x.copy())
        # 计算梯度
        grad = numerical_gradient(f, x)
        x -= lr * grad
        
    return x, np.array(x_history)

init_x = np.array([-3.0, 4.0])
lr = 0.1![062.png](https://upload-images.jianshu.io/upload_images/17476284-2603f60b731a6d85.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

step_num = 20
x, x_history = draw_gradient_descent(function_2, init_x, lr=lr, step_num=step_num)

plt.plot([-5, 5], [0, 0], '--b')
plt.plot([0, 0], [-5, 5], '--b')
plt.plot(x_history[:, 0], x_history[:, 1], 'o')
plt.xlabel('X0')
plt.ylabel('X1')
plt.show()

三、神经网络反向传播算法

1.计算图

计算图将计算过程用图形表示出来。这里说的图形是数据结构图,通过多个节点和边表示(连接节点的直线称为“边”)。



2.链式法则

反向传播将局部导数向正方向的反方向(从右到左)传递,传递这个局部导数的原理,是基于链式法则(chain rule)的。


3.激活函数层实现

sigmoid层反向传播

ReLu层反向传播

4.Affine/Softmax层实现

(1)Affine层(仿射层)

Affine层(单个数据)

Affine层(批量数据)
class Affine:
    def __init__(self, W, b):
        self.W = W
        self.b = b
        self.x = None
        self.dW = None
        self.db = None

    def forward(self, x):
        self.x = x
        out = np.dot(x, self.W) + self.b

        return out

    def backward(self, dout):
        dx = np.dot(dout, self.W.T)
        self.dW = np.dot(self.x.T, dout)
        self.db = np.sum(dout, axis=0)

        return dx

(2)Softmax-with-Loss 层

Softmax 层输出

Softmax-with-Loss 层
class SoftmaxWithLoss:
    def __init__(self):
        self.loss = None # 损失
        self.y = None    # softmax的输出
        self.t = None    # 监督数据(one-hot vector)

    def forward(self, x, t):
        self.t = t
        self.y = softmax(x)
        self.loss = cross_entropy_error(self.y, self.t)

        return self.loss

    def backward(self, dout=1):
        batch_size = self.t.shape[0]
        dx = (self.y - self.t) / batch_size

        return dx

四、神经网络最优化

1.参数更新

(1)随机梯度下降法(stochastic gradient descent,SGD)

class SGD:
    def __init__(self, lr=0.01):
        self.lr = lr

    def update(self, params, grads):
        for key in params.keys():
            params[key] -= self.lr * grads[key]

SGD缺点:在某些情况下效率低

(2)Momentum算法



W 表示要更新的权重参数,∂L/∂W表示损失函数关于 W 的梯度,η 表示学习率。
这里新出现了一个变量 v,对应物理上的速度。

class Momentum:
    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None

    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():
                self.v[key] = np.zeros_like(val)

        for key in params.keys():
            self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
            params[key] += self.v[key]

(3)AdaGrad算法

在神经网络的学习中,学习率(数学式中记为 η)的值很重要。学习率过小,会导致学习花费过多时间;反过来,学习率过大,则会导致学习发散而不能正确进行。

在关于学习率的有效技巧中,有一种被称为学习率衰减(learning rate decay)的方法,即随着学习的进行,使学习率逐渐减小。实际上,一开始“多”学,然后逐渐“少”学的方法,在神经网络的学习中经常被使用。

逐渐减小学习率的想法,相当于将“全体”参数的学习率值一起降低。而 AdaGrad [6] 进一步发展了这个想法,针对“一个一个”的参数,赋予其“定制”的值。

AdaGrad 会为参数的每个元素适当地调整学习率,与此同时进行学习(AdaGrad 的 Ada 来自英文单词 Adaptive,即“适当的”的意思)。



这里新出现了变量 h,如式 (6.5) 所示,它保存了以前的所有梯度值的平方和(式(6.5)中的⊙表示对应矩阵元素的乘法)。

class AdaGrad:
    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None

    def update(self, params, grads):
        if self.h is None:
        self.h = {}
        for key, val in params.items():
            self.h[key] = np.zeros_like(val)

    for key in params.keys():
        self.h[key] += grads[key] * grads[key]
        params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

2.权重初始值

权重初始化不能为全0初始化,否则会导致梯度下降法失效。
也不能将权重初始值设成一样的值,否则在误差反向传播法中,所有的权重值都会进行相同的更新,导致多结点无意义。
为了防止“权重均一化”(严格地讲,是为了瓦解权重的对称结构),必须随机生成初始值。

(1)Xavier初始值

node_num = 100 # 前一层的节点数
w = np.random.randn(node_num, node_num) / np.sqrt(node_num)

权重初始值为标准差是0.01的高斯分布时

权重初始值为Xavier初始值时

sigmoid函数、tanh函数等S形函数作为激活函数时适用Xavier初始值。

(2)He初始值

当激活函数使用 ReLU 时,一般推荐使用 ReLU 专用的初始值,也就是 Kaiming He 等人推荐的初始值,也称为“He 初始值”。
当前一层的节点数为 n 时,He 初始值使用标准差为√(2/n)的高斯分布。

node_num = 100 # 前一层的节点数
w = np.random.randn(node_num, node_num) * np.sqrt(2 / node_num)


ReLu函数作为激活函数时适用He初始值。

3.批标准化(Batch Normalization)



这里对 mini-batch 的 m 个输入数据的集合 B={x1, x2···, xm}求均值μB和方差σB^2。然后,对输入数据进行均值为 0、方差为 1(合适的分布)的正规化。ε 为一个极小值,防止分母为0。
Batch Norm 层会对正规化后的数据进行缩放和平移的变换。


4.正则化

(1)过拟合

过拟合原因

  • 模型拥有大量参数、表现力强。
  • 训练数据少。

(2)权值衰减

权值衰减是一直以来经常被使用的一种抑制过拟合的方法。该方法通过在学习的过程中对大的权重进行惩罚,来抑制过拟合。很多过拟合原本就是因为权重参数取值过大才发生的。
在求权重梯度的计算中,要为之前的误差反向传播法的结果加上正则化项的导数λW。

(3)Dropout(随机失活)

Dropout 是一种在学习的过程中随机删除神经元的方法。训练时,随机选出隐藏层的神经元,然后将其删除。被删除的神经元不再进行信号的传递,如下图所示。训练时,每传递一次数据,就会随机选择要删除的神经元。然后,测试时,虽然会传递所有的神经元信号,但是对于各个神经元的输出,要乘上训练时的删除比例后再输出。


class Dropout:
    def __init__(self, dropout_ratio=0.5):
        self.dropout_ratio = dropout_ratio
        self.mask = None

    def forward(self, x, train_flg=True):
        if train_flg:
            self.mask = np.random.rand(*x.shape) > self.dropout_ratio
            return x * self.mask
        else:
            return x * (1.0 - self.dropout_ratio)

    def backward(self, dout):
        return dout * self.mask

5.超参数验证

  • 步骤 0:设定超参数的范围。
  • 步骤 1:从设定的超参数范围中随机采样。
  • 步骤 2:使用步骤 1 中采样到的超参数的值进行学习,通过验证数据评估识别精度(但是要将 epoch 设置得很小)。
  • 步骤3:重复步骤 1 和步骤 2(100 次等),根据它们的识别精度的结果,缩小超参数的范围。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容