Haste makes waste

Nano01(自動運転)-U03-Lesson11-Neural Networks 神经网络

Posted on By lijun

1. 回顾之前的机器学习

  • 线性回归 Linear Regression

image

  • 逻辑回归 Logistic Regression

上面的线性回归,预测一个连续的值,而逻辑回归用于预测一个离散的值,常用于分类问题,逻辑回归常用于自动驾驶中,比如判断一个物体是车还是行人,红灯还是绿灯等。

image

下面实际上是感知机的原理:

image

4. A Note on Deep Learning

接下来的学习中将设计神经网络的初级到中级,将使用TensorFlow和卷积神经网络,去从头构建一个神经网络:

  1. 神经网络简介(本章)
  2. MiniFlow
  3. TensorFlow入门
  4. 深度神经网络
  5. 卷积神经网络

12. Perceptrons 感知机

关于深度学习中的感知机,可以参考 here

image

上图就是一个感知机,w1,w2,wn是权重,b是偏置,第一个Linear function线性函数通过权重和偏置计算结果,传递给第二个节点即step function阶跃函数,通过阶跃函数判断。这里阶跃函数是激活函数的一种。

15. Perceptrons as Logical Operators

AND Perceptron:

下面是关于and感知机的实现,有些python技巧很实用

  1. outputs 是一个list,这个list内的元素也是list
  2. num_wrong = len([output[4] for output in outputs if output[4] == 'No']) 获取为No的个数
import pandas as pd

# TODO: Set weight1, weight2, and bias
weight1 = 1.0
weight2 = 1.0
bias = -1.5


# DON'T CHANGE ANYTHING BELOW
# Inputs and outputs
test_inputs = [(0, 0), (0, 1), (1, 0), (1, 1)]
correct_outputs = [False, False, False, True]
outputs = []

# Generate and check output
for test_input, correct_output in zip(test_inputs, correct_outputs):
    linear_combination = weight1 * test_input[0] + weight2 * test_input[1] + bias
    output = int(linear_combination >= 0)
    is_correct_string = 'Yes' if output == correct_output else 'No'
    outputs.append([test_input[0], test_input[1], linear_combination, output, is_correct_string])

# Print output
num_wrong = len([output[4] for output in outputs if output[4] == 'No'])
output_frame = pd.DataFrame(outputs, columns=['Input 1', '  Input 2', '  Linear Combination', '  Activation Output', '  Is Correct'])
if not num_wrong:
    print('Nice!  You got it all correct.\n')
else:
    print('You got {} wrong.  Keep trying!\n'.format(num_wrong))
print(output_frame.to_string(index=False))

输出如下:

Nice!  You got it all correct.

Input 1    Input 2    Linear Combination    Activation Output   Is Correct
      0          0                  -1.5                    0          Yes
      0          1                  -0.5                    0          Yes
      1          0                  -0.5                    0          Yes
      1          1                   0.5                    1          Yes

用相同的方式,可以实现OR,NOT感知机。

异或门XOR,可以用上述的组合来实现:

def XOR(x1,x2):
    s1 = NAND(x1,x2)
    s2 = OR(x1,x2)
    y = AND2(s1,s2)
    return y

参考下图:

image

17. Perceptron Algorithm

image

上面的实现代码如下:

import numpy as np
# Setting the random seed, feel free to change it and see different solutions.
np.random.seed(42)

def stepFunction(t):
    if t >= 0:
        return 1
    return 0

def prediction(X, W, b):
    return stepFunction((np.matmul(X,W)+b)[0])

# TODO: Fill in the code below to implement the perceptron trick.
# The function should receive as inputs the data X, the labels y,
# the weights W (as an array), and the bias b,
# update the weights and bias W, b, according to the perceptron algorithm,
# and return W and b.
def perceptronStep(X, y, W, b, learn_rate = 0.01):
    for i in range(len(X)):
        y_hat = prediction(X[i],W,b)
        if y[i]-y_hat == 1:
            W[0] += X[i][0]*learn_rate
            W[1] += X[i][1]*learn_rate
            b += learn_rate
        elif y[i]-y_hat == -1:
            W[0] -= X[i][0]*learn_rate
            W[1] -= X[i][1]*learn_rate
            b -= learn_rate
    return W, b

    
# This function runs the perceptron algorithm repeatedly on the dataset,
# and returns a few of the boundary lines obtained in the iterations,
# for plotting purposes.
# Feel free to play with the learning rate and the num_epochs,
# and see your results plotted below.
def trainPerceptronAlgorithm(X, y, learn_rate = 0.01, num_epochs = 25):
    x_min, x_max = min(X.T[0]), max(X.T[0])
    y_min, y_max = min(X.T[1]), max(X.T[1])
    W = np.array(np.random.rand(2,1))
    b = np.random.rand(1)[0] + x_max
    # These are the solution lines that get plotted below.
    boundary_lines = []
    for i in range(num_epochs):
        # In each epoch, we apply the perceptron step.
        W, b = perceptronStep(X, y, W, b, learn_rate)
        boundary_lines.append((-W[0]/W[1], -b/W[1]))
    return boundary_lines

image

上面函数的调用构成如下图:

image

  1. trainPerceptronAlgorithm(),根据指定的学习率和迭代次数,计算调整后的权值和偏置。
  2. perceptronStep(),根据指定的学习率,以及预测结果及标签,调整权值和偏置。
    • y[i] = 1 (标签为正),y_hat = 0(预测为负),用加法调整权值和偏置
    • y[i] = 0 (标签为负),y_hat = 1(预测为正),用减法法调整权值和偏置
  3. prediction(),调用激活函数(阶跃函数),得到最终的输出,即预测结果
  4. stepFunction(),最后的激活函数-阶跃函数

调整学习率和迭代次数结果如下:

image

可以看出,如何设定学习率和迭代次数,对最终结果有直接影响:

  1. 学习率过大(0.1),会导致每一步的步幅过大,难以得到正确的结果。
  2. 迭代次数过少(5),会导致还没有达到最好效果就结束了;迭代次数过大(100),会做无用功。

20. Log-loss Error Function 误差函数(sigmoid函数)

参考损失函数(lost function) 关于sigmoid函数,参考2.2 sigmoid函数

一个误差函数要保证连续可求导()两个条件,即:

  • The error function should be differentiable.
  • The error function should be continuous.

基于上面损失函数的规律,所以激活函数要有连续可到的特性,如下图,将激活函数从阶跃函数(离散)变为了sigmoid函数(连续),输出结果从是否被录取,变成了录取的概率:

image

参考下图:

image

  • 连续可导的激活函数sigmoid函数,在x无穷大时,结果趋于1,x为无穷小时,结果趋于0。

代码实现如下:

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

练习题:

The sigmoid function is defined as sigmoid(x) = 1/(1+e-x). If the score is defined by 4x1 + 5x2 - 9 = score, then which of the following points has exactly a 50% probability of being blue or red? (Choose all that are correct.)

答案是 (1,1)(-4,5),即只要得到了score为0,则最终的sigmoid函数结果为0.5.

image

21. Softmax

上面介绍了阶跃函数和sigmoid函数,分别表示是否录取,或是录取的概率,那如果输出有多个结果呢,比如录取的是北大,还是清华,或是复旦,这里要用到Softmax函数。

5.1 softmax函数

def softmax(L):
    expL = np.exp(L)
    sumExpL = sum(expL)
    result = []
    for i in expL:
        result.append(i*1.0/sumExpL)
    return result

image

22. One-Hot Encoding

这种方式叫One-hot Encoding,在处理数据时经常使用。

image

如上图中,针对每一种结果,都有一个标签,这个标签是一个list,这个list中包含的元素个数,与结果的总数相同,然后在对应的位置设为1.

在另一个手写数字识别中也是这样做的,有0-9共10种测试结果,比如0的标签为[1,0,0,0,0,0,0,0,0]

23. Maximum Likelihood 最大似然法

如下图,分别是两种模型的预测结果,通过两种模型结果中,各个预测值的概率乘积总和,判断哪种模型更加准确,这种方式叫做最大似然法

image

24. Maximizing Probabilities

We will learn how to maximize a probability, using some math. Nothing more than high school math, so get ready for a trip down memory lane!

参考上面的图,如果将所有概率乘起来的话,其结果将是一个很小的数,我们需要将其乘积转换为和的形式,通过log函数,可以将乘积变成求和的形式,如下:

  • log(p1*p2*p3*p4..*pn*) = logp1 + logp2 + logp3 + .. logpn

25. Cross-Entropy 交叉熵

上面将乘积形式转换后的求和形式,叫做交叉熵。模型越准确,交叉熵越小,参考下图:

image

如下用示例解释交叉熵:

image

(1,1,0)表示标签,即前面两个有礼物,后一个无礼物;(0.8,0.7,0.1)表示出现这种标签的概率

用代码实现如下:

import numpy as np

def cross_entropy(Y, P):
    Y = np.float_(Y)
    P = np.float_(P)
    return -np.sum(Y * np.log(P) + (1 - Y) * np.log(1 - P))

27. Multi-Class Cross Entropy

上面的结果中只有是和否,如果结果是多个的情况下,用如下的交叉熵公式:

image

28. Logistic Regression 逻辑回归

逻辑回归大致步骤如下:

  1. 准备数据
  2. 选取一个随机模型
  3. 计算误差
  4. 最小化误差,优化模型

下面分别是二元分类和多元分类问题的误差函数:

image

通过梯度下降的方法,找到其对应的最小误差:

image

梯度的计算方法,可以参考4.1 梯度法

在某点的上方和下方(极小处),分别取一个点,求这两个点之间的斜率。

29. Gradient Descent 梯度下降

参考4.1 梯度法

image

参考上图,每次通过学习率和导数去调整权重和偏置,得到更好的权重和偏置。

梯度计算公式推导:

上面几个部分中,我们了解到要最小化损失函数,就要计算其导数,下面实际推导一下误差函数的导数。

无语了,全是微积分的推导,这些基本公式都忘了,不理解…微积分得从头补起来,不然真是空中楼阁。

image

30. Gradient Descent: The Code

权重的更新值为:Δwi=α∗δ∗xi,其中α是学习率,δ是误差项。

相关代码如下:

# Defining the sigmoid function for activations
def sigmoid(x):
    return 1/(1+np.exp(-x))

# Derivative of the sigmoid function
def sigmoid_prime(x):
    return sigmoid(x) * (1 - sigmoid(x))

# Input data
x = np.array([0.1, 0.3])
# Target
y = 0.2
# Input to output weights
weights = np.array([-0.8, 0.5])

# The learning rate, eta in the weight step equation
learnrate = 0.5

# The neural network output (y-hat)
nn_output = sigmoid(x[0]*weights[0] + x[1]*weights[1])
# or nn_output = sigmoid(np.dot(x, weights))

# output error (y - y-hat)
error = y - nn_output

# error term (lowercase delta)
error_term = error * sigmoid_prime(np.dot(x,weights))

# Gradient descent step 
del_w = [ learnrate * error_term * x[0],
                 learnrate * error_term * x[1]]
# or del_w = learnrate * error_term * x
  • error_term是误差项δ,通过error * sigmoid_prime(np.dot(x,weights))计算得到。
  • del_w是权重的更新值。

练习的代码:

import numpy as np

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

def sigmoid_prime(x):
    return sigmoid(x) * (1 - sigmoid(x))

learnrate = 0.5
x = np.array([1, 2])
y = np.array(0.5)

# Initial weights
w = np.array([0.5, -0.5])

# Calculate one gradient descent step for each weight
# TODO: Calculate output of neural network
nn_output = sigmoid(np.dot(x, w))
# nn_output = sigmoid(x[0]*w[0] + x[1]*w[1])

# TODO: Calculate error of neural network
error = y - nn_output

# TODO: Calculate change in weights
del_w = learnrate * error * nn_output * (1 - nn_output) * x

print('Neural Network output:')
print(nn_output)
print('Amount of Error:')
print(error)
print('Change in Weights:')
print(del_w)

输出结果如下:

Neural Network output:
0.3775406687981454
Amount of Error:
0.1224593312018546
Change in Weights:
[0.0143892 0.0287784]

Nice job!  That's right!

31. Perceptron感知机 vs Gradient Descent梯度下降

感知机和梯度下降参考如下:

下图是感知机和梯度下降的区别:

  • 感知机只有在分类错误时,才去调整权重,而梯度下降会不断调整权重。
  • 感知机使用的是阶跃函数,预测值y和标签的y,要么是1,要么是0;而梯度下降是一个概率值在0-1之间。
  • 感知机的权值调整,可以看做是梯度下降的一种特殊case,感知机只有两种情况,wi + axiwi - axi

image

35. Neural Network Architecture 神经网络结构

35.1 简单结构的神经网络

构建一个非线性的神经网络结构:

  1. 通过两个线性模型的叠加求和,最终使用sigmoid激活函数,得到新的非线性模型,如下图所示:

image

  1. 通过给线性模型添加权重和偏置,得到更复杂的新模型:

image

将结构图修改如下,就有点像神经网络的样子了:

image

练习:

Based on the above video, let’s define the combination of two new perceptrons as w10.4 + w20.6 + b. Which of the following values for the weights and the bias would result in the final probability of the point to be 0.88?

w1:2,w2:6;b:-2 w1:3,w2:5;b:-2.2 w1:5,w2:4;b:-3

将上面三个选项,代入上面的线性模型,分别得到2.4,2,1.4 三个值,然后代入sigmoid函数求解,代码如下:

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

print(sigmoid(2.4))
print(sigmoid(2))
print(sigmoid(1.4))

得到结果为如下,所以第二个选项正确:

0.916827303506
0.880797077978
0.802183888559

35.2 Multiple layers 多层结构的神经网络

上面的神经网络比较简单,如下的网络更加复杂:

  • 给输入层,隐藏层,输出层添加更多的节点
  • 添加更多的隐藏层

如下图:

image

image

多元分类:

一个输入,最终有多个输出,输出每个种类出现的概率,或是softmax函数值:

image

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

参考手写图片的数字识别处理神经网络的推理处理 输出层有10个结果,分别代码是0-9的概率或是其他的指标值。

36. Feedforward 前向反馈

通过权重/偏置/激活函数,最终计算出预测值,这就是神经网络的前向反馈。

image

Error Function 误差函数:

image

37. Multilayer Perceptrons 多层感知机

下面介绍多层感知机神经网络中的数学知识,下面将使用到vector向量和矩阵matrice。

现在我们处理的这个感知机,有复数个input,复数个隐藏层的node,在这两者之间的权重,要满足:

  • Wij,i表示行数,j表示列数,i与input的元素个数相同,j与隐藏层个数相同。

image

参考如下代码:

# Number of records and input units
n_records, n_inputs = features.shape
# Number of hidden units
n_hidden = 2
weights_input_to_hidden = np.random.normal(0, n_inputs**-0.5, size=(n_inputs, n_hidden))

上面生成了一个input层到hidden层的权重矩阵,hidden层上的各个unit的计算方式为:

image

代码如下:

hidden_inputs = np.dot(inputs, weights_input_to_hidden)

image

通过上面的图可以很好的理解,为什么权重矩阵的行数与input的列数一样,权重矩阵的列数与隐藏层的列数一样。

image

矩阵的翻转:

matrix = [[2,3,4],[5,6,7]]
matrix = np.array(matrix, ndmin=2)
print(matrix)
print("--------------")
print(matrix.T)

如下:

matrix = [[2,3,4],[5,6,7]]
matrix = np.array(matrix, ndmin=2)
print(matrix,"===",matrix.shape)
print("--------------")
print(matrix.T,"===",matrix.T.shape)

翻转后结果如下:

[[2 3 4]
 [5 6 7]] === (2, 3)
--------------
[[2 5]
 [3 6]
 [4 7]] === (3, 2)
matrix = [5,6,7]
matrix = np.array(matrix)
print(matrix,"===",matrix.shape)
print("--------------")
print(matrix.T,"===",matrix.T.shape)

如果是一维的,结果不变:

[5 6 7] === (3,)
--------------
[5 6 7] === (3,)

Programming quiz:

Below, you’ll implement a forward pass through a 4x3x2 network, with sigmoid activation functions for both layers.

Things to do:

  • Calculate the input to the hidden layer.
  • Calculate the hidden layer output.
  • Calculate the input to the output layer.
  • Calculate the output of the network.

代码:

import numpy as np

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

# Network size
N_input = 4
N_hidden = 3
N_output = 2

np.random.seed(42)
# Make some fake data
X = np.random.randn(4)

weights_input_to_hidden = np.random.normal(0, scale=0.1, size=(N_input, N_hidden))
weights_hidden_to_output = np.random.normal(0, scale=0.1, size=(N_hidden, N_output))


# TODO: Make a forward pass through the network

hidden_layer_in = np.dot(X, weights_input_to_hidden)
hidden_layer_out = sigmoid(hidden_layer_in)

print('Hidden-layer Output:')
print(hidden_layer_out)

output_layer_in = np.dot(hidden_layer_out, weights_hidden_to_output)
output_layer_out = sigmoid(output_layer_in)

print('Output-layer Output:')
print(output_layer_out)
Hidden-layer Output:
[0.41492192 0.42604313 0.5002434 ]
Output-layer Output:
[0.49815196 0.48539772]

Nice job!  That's right!

image

38. Backpropagation 反向传播

现在该轮到我们实际的训练一个神经网络,在这里我们使用一种叫反向传播的方式,简而言之,反向传播包括如下的几点:

  1. 进行正向反馈操作
  2. 将模型输出与期望输出(标签)进行对比
  3. 计算误差
  4. 使用反向传播将误差反映到权重上
  5. 用这个去更新权重,得到一个更好的模型
  6. 循环上述步骤,至到得到一个满意的模型

反向传播是深度学习的基础,TensorFlow和其他一些库虽说已经将其封装完好,但是自己还是要理解其算法。

可以参考误差反向传播