文档章节

(三)神经网络入门之隐藏层设计

AllenOR灵感
 AllenOR灵感
发布于 2017/09/10 01:26
字数 3315
阅读 1
收藏 0
点赞 0
评论 0

这篇教程是翻译Peter Roelants写的神经网络教程,作者已经授权翻译,这是原文

该教程将介绍如何入门神经网络,一共包含五部分。你可以在以下链接找到完整内容。

隐藏层


这部分教程将介绍三部分:

  • 隐藏层设计
  • 非线性激活函数
  • BP算法

在前面几个教程中,我们已经介绍了一些很简单的教程,就是单一的回归模型或者分类模型。在这个教程中,我们也将设计一个二分类神经网络模型,其中输入数据是一个维度,隐藏层只有一个神经元,并且使用非线性函数作为激活函数,模型结构能用图表示为:


我们先导入教程需要使用的软件包。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import colorConverter, ListedColormap 
from mpl_toolkits.mplot3d import Axes3D 
from matplotlib import cm
定义数据集

在这篇教程中,我们将输入数据x分类成两个类别,用蓝色表示t = 1,用红色表示t = 0。其中,红色分类样本是一个多峰分布,被蓝色分类样本包围。这些数据都是一维的,但是数据之间的间隔并不是线性的分割。这些数据特性将在下图中表示出来。

这个二分类模型不会完全准确的分类处理啊,因为我们在其中加入了一个神经元,并且采用的是非线性函数。

# Define and generate the samples
nb_of_samples_per_class = 20  # The number of sample in each class
blue_mean = [0]  # The mean of the blue class
red_left_mean = [-2]  # The mean of the red class
red_right_mean = [2]  # The mean of the red class

std_dev = 0.5  # standard deviation of both classes
# Generate samples from both classes
x_blue = np.random.randn(nb_of_samples_per_class, 1) * std_dev + blue_mean
x_red_left = np.random.randn(nb_of_samples_per_class/2, 1) * std_dev + red_left_mean
x_red_right = np.random.randn(nb_of_samples_per_class/2, 1) * std_dev + red_right_mean

# Merge samples in set of input variables x, and corresponding set of
# output variables t
x = np.vstack((x_blue, x_red_left, x_red_right))
t = np.vstack((np.ones((x_blue.shape[0],1)), 
               np.zeros((x_red_left.shape[0],1)), 
               np.zeros((x_red_right.shape[0], 1))))
# Plot samples from both classes as lines on a 1D space
plt.figure(figsize=(8,0.5))
plt.xlim(-3,3)
plt.ylim(-1,1)
# Plot samples
plt.plot(x_blue, np.zeros_like(x_blue), 'b|', ms = 30) 
plt.plot(x_red_left, np.zeros_like(x_red_left), 'r|', ms = 30) 
plt.plot(x_red_right, np.zeros_like(x_red_right), 'r|', ms = 30) 
plt.gca().axes.get_yaxis().set_visible(False)
plt.title('Input samples from the blue and red class')
plt.xlabel('$x$', fontsize=15)
plt.show()

输入样本

非线性激活函数

在这里,我们使用的非线性转换函数是Gaussian radial basis function (RBF)。除了径向基函数网络,RBF函数在神经网络中不经常被作为激活函数。比较常见的激活函数是sigmoid函数。但我们根据设计的输入数据x,在这里RBF函数能很好地将蓝色样本数据从红色样本数据中分类出来,下图画出了RBF函数的图像。RBF函数给定义为:


RBF函数

RBF函数的导数为定义为:


RBF函数的导数
# Define the rbf function
def rbf(z):
    return np.exp(-z**2)
# Plot the rbf function
z = np.linspace(-6,6,100)
plt.plot(z, rbf(z), 'b-')
plt.xlabel('$z$', fontsize=15)
plt.ylabel('$e^{-z^2}$', fontsize=15)
plt.title('RBF function')
plt.grid()
plt.show()

RBF函数可视化

BP算法

在训练模型的时候,我们使用BP算法来进行模型优化,这是一种很典型的优化算法。BP算法的每次迭代分为两步:

  1. 正向传播去计算神经网络的输出。
  2. 利用神经网络得出的结果和真实结果之间的误差进行反向传播来更新神经网络的参数。

1. 正向传播

在计算正向传播中,输入数据被一层一层的计算,最后从模型中得出输出结果。

计算隐藏层的激活函数

隐藏层h经激活函数之后,输出结果为:


激活函数

其中,wh是权重参数。hidden_activations(x, wh)函数实现了该功能。

计算输出结果的激活函数

神经网络的最后一层的输出,是将隐藏层的输出h作为数据参数,并且利用Logistic函数来作为激活函数。


输出结果的激活函数

其中,w0是输出层的权重,output_activations(h, w0)函数实现了该功能。我们在公式中添加了一个偏差项-1,因为如果不添加偏差项,那么Logistic函数只能学到一个经过原点的分类面。因为,隐藏层中的RBF函数的输入值得范围是从零到正无穷,那么如果我们不在输出层加上偏差项的话,模型不可能学出有用的分类结果,因为没有样本的值将小于0,从而归为决策树的左边。因此,我们增加了一个截距,即偏差项。正常情况下,偏差项也和权重参数一样,需要被训练,但是由于这个例子中的模型非常简单,所以我们就用一个常数来作为偏差项。

# Define the logistic function
def logistic(z): 
    return 1 / (1 + np.exp(-z))

# Function to compute the hidden activations
def hidden_activations(x, wh):
    return rbf(x * wh)

# Define output layer feedforward
def output_activations(h , wo):
    return logistic(h * wo - 1)

# Define the neural network function
def nn(x, wh, wo): 
    return output_activations(hidden_activations(x, wh), wo)

# Define the neural network prediction function that only returns
#  1 or 0 depending on the predicted class
def nn_predict(x, wh, wo): 
    return np.around(nn(x, wh, wo))

2. 反向传播

在反向传播过程中,我们需要先计算出神经网络的输出与真实值之间的误差。这个误差会一层一层的反向传播去更新神经网络中的各个权重。

在每一层中,使用梯度下降算法按照负梯度方向对每个参数进行更新。

参数whwo利用w(k+1)=w(k)−Δw(k+1)更新,其中Δw=μ∗∂ξ/∂wμ是学习率,∂ξ/∂w是损失函数ξ对参数w的梯度。

计算损失函数

在这个模型中,损失函数ξ与交叉熵损失函数一样,具体解释在这里


损失函数

损失函数对于参数whwo的表示如下图所示。从图中,我们发现误差面不是一个凸函数,而且沿着wh = 0这一轴,参数wh将是损失函数的一个映射。

从图中发现,沿着wh = 0,从wo > 0开始,损失函数有一个非常陡峭的梯度,并且我们要按照图形的下边缘进行梯度下降。如果学习率取得过大,那么在梯度更新的时候,可能跳过最小值,从一边的梯度方向跳到另一边的梯度方向。因为梯度的方向太陡峭了,每次对参数的更新跨度将会非常大。因此,在开始的时候我们需要将学习率取一个比较小的值。

# Define the cost function
def cost(y, t):
    return - np.sum(np.multiply(t, np.log(y)) + np.multiply((1-t), np.log(1-y)))

# Define a function to calculate the cost for a given set of parameters
def cost_for_param(x, wh, wo, t):
    return cost(nn(x, wh, wo) , t)
# Plot the cost in function of the weights
# Define a vector of weights for which we want to plot the cost
nb_of_ws = 200 # compute the cost nb_of_ws times in each dimension
wsh = np.linspace(-10, 10, num=nb_of_ws) # hidden weights
wso = np.linspace(-10, 10, num=nb_of_ws) # output weights
ws_x, ws_y = np.meshgrid(wsh, wso) # generate grid
cost_ws = np.zeros((nb_of_ws, nb_of_ws)) # initialize cost matrix
# Fill the cost matrix for each combination of weights
for i in range(nb_of_ws):
    for j in range(nb_of_ws):
        cost_ws[i,j] = cost(nn(x, ws_x[i,j], ws_y[i,j]) , t)
# Plot the cost function surface
fig = plt.figure()
ax = Axes3D(fig)
# plot the surface
surf = ax.plot_surface(ws_x, ws_y, cost_ws, linewidth=0, cmap=cm.pink)
ax.view_init(elev=60, azim=-30)
cbar = fig.colorbar(surf)
ax.set_xlabel('$w_h$', fontsize=15)
ax.set_ylabel('$w_o$', fontsize=15)
ax.set_zlabel('$\\xi$', fontsize=15)
cbar.ax.set_ylabel('$\\xi$', fontsize=15)
plt.title('Cost function surface')
plt.grid()
plt.show()

损失函数误差表面
输出层更新

∂ξi/∂wo是每个样本i的输出梯度,参照第二部分教程的方法,我们可以得出相应的推导公式:


梯度推导

其中,zoi=hi∗wohi是样本i经过激活函数之后输出的值,∂ξi/∂zoi=δoi是输出层误差的求导。

gradient_output(y, t)函数实现了δogradient_weight_out(h, grad_output)函数实现了∂ξ/∂wo

隐藏层更新

∂ξi/∂wh是每个样本i在影藏层的梯度,具体计算如下:


隐藏层梯度推导

其中,


∂ξi/∂zhi=δhi表示误差对于隐藏层输入的梯度。这个误差也可以解释为,zhi对于最后误差的贡献。那么,接下来我们定义一下这个误差梯度δhi


误差梯度

又应为∂zhi/∂wh=xi,那么我们能计算最后的值为:


梯度计算

在批处理中,对每个对应参数的梯度进行累加,就是最后的梯度。

gradient_hidden(wo, grad_output)函数实现了δh
gradient_weight_hidden(x, zh, h, grad_hidden)函数实现了∂ξ/∂wh
backprop_update(x, t, wh, wo, learning_rate)函数实现了BP算法的每次迭代过程。

# Define the error function
def gradient_output(y, t):
    return y - t

# Define the gradient function for the weight parameter at the output layer
def gradient_weight_out(h, grad_output): 
    return  h * grad_output

# Define the gradient function for the hidden layer
def gradient_hidden(wo, grad_output):
    return wo * grad_output

# Define the gradient function for the weight parameter at the hidden layer
def gradient_weight_hidden(x, zh, h, grad_hidden):
    return x * -2 * zh * h * grad_hidden

# Define the update function to update the network parameters over 1 iteration
def backprop_update(x, t, wh, wo, learning_rate):
    # Compute the output of the network
    # This can be done with y = nn(x, wh, wo), but we need the intermediate 
    #  h and zh for the weight updates.
    zh = x * wh
    h = rbf(zh)  # hidden_activations(x, wh)
    y = output_activations(h, wo)
    # Compute the gradient at the output
    grad_output = gradient_output(y, t)
    # Get the delta for wo
    d_wo = learning_rate * gradient_weight_out(h, grad_output)
    # Compute the gradient at the hidden layer
    grad_hidden = gradient_hidden(wo, grad_output)
    # Get the delta for wh
    d_wh = learning_rate * gradient_weight_hidden(x, zh, h, grad_hidden)
    # return the update parameters
    return (wh-d_wh.sum(), wo-d_wo.sum())
BP算法更新

下面的代码,我们模拟了一个50次的循环。白色的点表示,参数whwo在误差面上面的第k次迭代。

在更新过程中,我们不断的线性减小学习率。这是为了在更新到最后的时候,学习率能是0。这样能保证最后的参数更新不会在最小值附近徘徊。

# Run backpropagation
# Set the initial weight parameter
wh = 2
wo = -5
# Set the learning rate
learning_rate = 0.2

# Start the gradient descent updates and plot the iterations
nb_of_iterations = 50  # number of gradient descent updates
lr_update = learning_rate / nb_of_iterations # learning rate update rule
w_cost_iter = [(wh, wo, cost_for_param(x, wh, wo, t))]  # List to store the weight values over the iterations
for i in range(nb_of_iterations):
    learning_rate -= lr_update # decrease the learning rate
    # Update the weights via backpropagation
    wh, wo = backprop_update(x, t, wh, wo, learning_rate) 
    w_cost_iter.append((wh, wo, cost_for_param(x, wh, wo, t)))  # Store the values for plotting

# Print the final cost
print('final cost is {:.2f} for weights wh: {:.2f} and wo: {:.2f}'.format(cost_for_param(x, wh, wo, t), wh, wo))

在我们的机器上面,最后输出的结果是:
final cost is 10.81 for weights wh: 1.20 and wo: 5.56

但由于参数初始化的不同,可能在你的机器上面运行会有不同的结果。

# Plot the weight updates on the error surface
# Plot the error surface
fig = plt.figure()
ax = Axes3D(fig)
surf = ax.plot_surface(ws_x, ws_y, cost_ws, linewidth=0, cmap=cm.pink)
ax.view_init(elev=60, azim=-30)
cbar = fig.colorbar(surf)
cbar.ax.set_ylabel('$\\xi$', fontsize=15)

# Plot the updates
for i in range(1, len(w_cost_iter)):
    wh1, wo1, c1 = w_cost_iter[i-1]
    wh2, wo2, c2 = w_cost_iter[i]
    # Plot the weight-cost value and the line that represents the update 
    ax.plot([wh1], [wo1], [c1], 'w+')  # Plot the weight cost value
    ax.plot([wh1, wh2], [wo1, wo2], [c1, c2], 'w-')
# Plot the last weights
wh1, wo1, c1 = w_cost_iter[len(w_cost_iter)-1]
ax.plot([wh1], [wo1], c1, 'w+')
# Shoz figure
ax.set_xlabel('$w_h$', fontsize=15)
ax.set_ylabel('$w_o$', fontsize=15)
ax.set_zlabel('$\\xi$', fontsize=15)
plt.title('Gradient descent updates on cost surface')
plt.grid()
plt.show()

Gradient descent updates on cost surface

分类结果的可视化

下面的代码可视化了最后的分类结果。在输入空间域里面,蓝色和红色代表了最后的分类颜色。从图中,我们发现所有的样本都被正确分类了。

# Plot the resulting decision boundary
# Generate a grid over the input space to plot the color of the
#  classification at that grid point
nb_of_xs = 100
xs = np.linspace(-3, 3, num=nb_of_xs)
ys = np.linspace(-1, 1, num=nb_of_xs)
xx, yy = np.meshgrid(xs, ys) # create the grid
# Initialize and fill the classification plane
classification_plane = np.zeros((nb_of_xs, nb_of_xs))
for i in range(nb_of_xs):
    for j in range(nb_of_xs):
        classification_plane[i,j] = nn_predict(xx[i,j], wh, wo)
# Create a color map to show the classification colors of each grid point
cmap = ListedColormap([
        colorConverter.to_rgba('r', alpha=0.25),
        colorConverter.to_rgba('b', alpha=0.25)])

# Plot the classification plane with decision boundary and input samples
plt.figure(figsize=(8,0.5))
plt.contourf(xx, yy, classification_plane, cmap=cmap)
plt.xlim(-3,3)
plt.ylim(-1,1)
# Plot samples from both classes as lines on a 1D space
plt.plot(x_blue, np.zeros_like(x_blue), 'b|', ms = 30) 
plt.plot(x_red_left, np.zeros_like(x_red_left), 'r|', ms = 30) 
plt.plot(x_red_right, np.zeros_like(x_red_right), 'r|', ms = 30) 
plt.gca().axes.get_yaxis().set_visible(False)
plt.title('Input samples and their classification')
plt.xlabel('x')
plt.show()

分类结果

输入域的转换

为什么神经网络模型能利用最后的线性Logistic实现非线性的分类呢?关键原因是隐藏层的非线性RBF函数。RBF转换函数可以将靠近原点的样本(蓝色分类)的输出值大于0,而远离原点的样本(红色样本)的输出值接近0。如下图所示,红色样本的位置都在左边接近0的位置,蓝色样本的位置在远离0的位置。这个结果就是使用线性Logistic分类的。

同时注意,我们使用的高斯函数的峰值偏移量是0,也就是说,高斯函数产生的值是一个关于原点分布的数据。

# Plot projected samples from both classes as lines on a 1D space
plt.figure(figsize=(8,0.5))
plt.xlim(-0.01,1)
plt.ylim(-1,1)
# Plot projected samples
plt.plot(hidden_activations(x_blue, wh), np.zeros_like(x_blue), 'b|', ms = 30) 
plt.plot(hidden_activations(x_red_left, wh), np.zeros_like(x_red_left), 'r|', ms = 30) 
plt.plot(hidden_activations(x_red_right, wh), np.zeros_like(x_red_right), 'r|', ms = 30) 
plt.gca().axes.get_yaxis().set_visible(False)
plt.title('Projection of the input samples by the hidden layer.')
plt.xlabel('h')
plt.show()

RBF函数产生图

完整代码,点击这里

© 著作权归作者所有

共有 人打赏支持
AllenOR灵感
粉丝 10
博文 2139
码字总数 82983
作品 0
程序员
python在线神经网络实现手写字符识别系统

神经网络实现手写字符识别系统 一、课程介绍 1. 课程来源 本课程核心部分来自《500 lines or less》项目,作者是来自 Mozilla 的工程师 Marina Samuel,这是她的个人主页:http://www.marina...

oxuzhenyi ⋅ 2017/02/11 ⋅ 0

机器学习之softmax函数

Softmax分类函数 这篇教程是翻译Peter Roelants写的神经网络教程,作者已经授权翻译,这是原文。 该教程将介绍如何入门神经网络,一共包含五部分。你可以在以下链接找到完整内容。 (一)神经...

qq_37634812 ⋅ 2017/12/07 ⋅ 0

一文学会用 Tensorflow 搭建神经网络

cs224d-Day 6: 快速入门 Tensorflow 本文是学习这个视频课程系列的笔记,课程链接是 youtube 上的, 讲的很好,浅显易懂,入门首选, 而且在github有代码, 想看视频的也可以去他的优酷里的频...

aliceyangxi1987 ⋅ 2017/04/26 ⋅ 0

深度学习必须熟悉的算法之word2vector(一)

作者:milter 链接:https://www.jianshu.com/p/1405932293ea word2vector已经成为NLP领域的基石算法。作为一名AI 从业者,如果不能主动去熟悉该算法,应该感到脸红。本文是一篇翻译的文章,原...

机器学习算法与自然语言处理 ⋅ 05/08 ⋅ 0

神经网络入门指南

人工神经网络(ANN)是一种从信息处理角度对人脑神经元网络进行抽象从而建立的某种简单模型,按不同的连接方式组成不同的网络。其在语音识别、计算机视觉和文本处理等方面取得的突破性成果。...

【方向】 ⋅ 2017/12/02 ⋅ 0

零基础入门深度学习工作原理?看本文就对了!

  昨天为大家推荐了几本入门深度学习的必看书籍,错过的朋友请戳这里。   在入门学习深度学习之前,有必要先了解一下深度学习的工作原理。著名程序猿小哥 Radu Raicea 特意为入门学习者写...

深度学习 ⋅ 06/01 ⋅ 0

连载3

<神经网络入门>连载3 4. 聪明的扫雷机工程 (Smart Minesweeper Project) ....我要向你介绍的第一个完整例子,是怎么使用神经网络来控制具有人工智能的扫雷机的行为。扫雷机工作在一个很简单...

racoon ⋅ 2011/06/14 ⋅ 4

零基础入门深度学习(1) - 感知器

python深度学习大全 原文地址:https://www.zybuluo.com/hanbingtao/note/433855 深度学习是啥 在人工智能领域,有一个方法叫机器学习。在机器学习这个方法里,有一类算法叫神经网络。神经网...

luanpeng825485697 ⋅ 01/09 ⋅ 0

一篇文章搞懂循环神经网络

作者:忆臻 哈尔滨工业大学 自然语言处理专业硕博生 推荐阅读时间8min~15min 主要内容简介:神经网络基础、为什么需要RNN、RNN的具体结构、以及RNN应用和一些结论 1神经网络基础 神经网络可以...

bjweimengshu ⋅ 2017/11/26 ⋅ 0

零基础入门深度学习(3) - 神经网络和反向传播算法

python深度学习大全 原文地址:https://www.zybuluo.com/hanbingtao/note/476663 往期回顾 在上一篇文章中,我们已经掌握了机器学习的基本套路,对模型、目标函数、优化算法这些概念有了一定...

luanpeng825485697 ⋅ 01/09 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

笔试题之Java基础部分【简】【一】

基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语法,集合的语法,io 的语法,虚拟机方面的语法,其他 1.length、length()和size() length针对...

anlve ⋅ 10分钟前 ⋅ 1

table eg

user_id user_name full_name 1 zhangsan 张三 2 lisi 李四 `` ™ [========] 2018-06-18 09:42:06 星期一½ gdsgagagagdsgasgagadsgdasgagsa...

qwfys ⋅ 35分钟前 ⋅ 0

一个有趣的Java问题

先来看看源码: public class TestDemo { public static void main(String[] args) { Integer a = 10; Integer b = 20; swap(a, b); System.out......

linxyz ⋅ 39分钟前 ⋅ 0

十五周二次课

十五周二次课 17.1mysql主从介绍 17.2准备工作 17.3配置主 17.4配置从 17.5测试主从同步 17.1mysql主从介绍 MySQL主从介绍 MySQL主从又叫做Replication、AB复制。简单讲就是A和B两台机器做主...

河图再现 ⋅ 今天 ⋅ 0

docker安装snmp rrdtool环境

以Ubuntu16:04作为基础版本 docker pull ubuntu:16.04 启动一个容器 docker run -d -i -t --name flow_mete ubuntu:16.04 bash 进入容器 docker exec -it flow_mete bash cd ~ 安装基本软件 ......

messud4312 ⋅ 今天 ⋅ 0

OSChina 周一乱弹 —— 快别开心了,你还没有女友呢。

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @莱布妮子 :分享吴彤的单曲《好春光》 《好春光》- 吴彤 手机党少年们想听歌,请使劲儿戳(这里) @clouddyy :小萝莉街上乱跑,误把我认错成...

小小编辑 ⋅ 今天 ⋅ 8

Java 开发者不容错过的 12 种高效工具

Java 开发者常常都会想办法如何更快地编写 Java 代码,让编程变得更加轻松。目前,市面上涌现出越来越多的高效编程工具。所以,以下总结了一系列工具列表,其中包含了大多数开发人员已经使用...

jason_kiss ⋅ 昨天 ⋅ 0

Linux下php访问远程ms sqlserver

1、安装freetds(略,安装在/opt/local/freetds 下) 2、cd /path/to/php-5.6.36/ 进入PHP源码目录 3、cd ext/mssql进入MSSQL模块源码目录 4、/opt/php/bin/phpize生成编译配置文件 5、 . ./...

wangxuwei ⋅ 昨天 ⋅ 0

如何成为技术专家

文章来源于 -- 时间的朋友 拥有良好的心态。首先要有空杯心态,用欣赏的眼光发现并学习别人的长处,包括但不限于工具的使用,工作方法,解决问题以及规划未来的能力等。向别人学习的同时要注...

长安一梦 ⋅ 昨天 ⋅ 0

Linux vmstat命令实战详解

vmstat命令是最常见的Linux/Unix监控工具,可以展现给定时间间隔的服务器的状态值,包括服务器的CPU使用率,内存使用,虚拟内存交换情况,IO读写情况。这个命令是我查看Linux/Unix最喜爱的命令...

刘祖鹏 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部