该教程将介绍如何入门神经网络,一共包含五部分。你可以在以下链接找到完整内容。
(一)神经网络入门之线性回归
Logistic分类函数
(二)神经网络入门之Logistic回归(分类问题)
(三)神经网络入门之隐藏层设计
Softmax分类函数
(四)神经网络入门之矢量化
(五)神经网络入门之构建多层网络
这篇教程中的代码是由 Python 2 IPython Notebook产生的,在教程的最后,我会给出全部代码的链接,帮助学习。神经网络中有关矩阵的运算我们采用NumPy来构建,画图使用Matplotlib来构建。如果你来没有按照这些软件,那么我强烈建议你使用Anaconda Python来安装,这个软件包中包含了运行这个教程的所有软件包,非常方便使用。
我们先导入教程需要的软件包
from __future__ import print_function
import numpy as np
import matplotlib.pyplot as plt
线性回归
本教程主要包含三部分:
- 一个非常简单的神经网络
- 一些概念,比如目标函数,损失函数
- 梯度下降
x来预测一个真实结果t。神经网络的模型结构为y = x * w,其中x是输入参数,w是权重,y是预测结果。神经网络的模型可以被表示为下图:
w的层,并且没有激活函数和偏差单元。在简单线性回归中,权重w和偏差单元一般都写成一个参数向量β,其中偏差单元是y轴上面的截距,w是回归线的斜率。在线性回归中,我们一般使用最小二乘法来优化这些参数。y和正确结果t尽可能的接近。损失函数我们定义为:
损失函数
对于损失函数的优化,我们采用梯度下降,这个方法是神经网络中常见的优化方法。
定义目标函数
f来产生目标结果t,但是对目标结果加上一些高斯噪声N(0, 0.2),其中N表示正态分布,均值是0,方差是0.2,f定义为f(x) = 2x,x是输入参数,回归线的斜率是2,截距是0。所以最后的t = f(x) + N(0, 0.2)。x,然后设计目标结果t。下面的程序我们生成了x和t,以及画出了他们之间的线性关系。
# Define the vector of input samples as x, with 20 values sampled from a uniform distribution
# between 0 and 1
x = np.random.uniform(0, 1, 20)
# Generate the target values t from x with small gaussian noise so the estimation won't be perfect.
# Define a function f that represents the line that generates t without noise
def f(x): return x * 2
# Create the targets t with some gaussian noise
noise_variance = 0.2 # Variance of the gaussian noise
# Gaussian noise error for each sample in x
noise = np.random.randn(x.shape[0]) * noise_variance
# Create targets t
t = f(x) + noise
# Define the vector of input samples as x, with 20 values sampled from a uniform distribution
# between 0 and 1
x = np.random.uniform(0, 1, 20)
# Generate the target values t from x with small gaussian noise so the estimation won't be perfect.
# Define a function f that represents the line that generates t without noise
def f(x): return x * 2
# Create the targets t with some gaussian noise
noise_variance = 0.2 # Variance of the gaussian noise
# Gaussian noise error for each sample in x
noise = np.random.randn(x.shape[0]) * noise_variance
# Create targets t
t = f(x) + noise
# Plot the target t versus the input x
plt.plot(x, t, 'o', label='t')
# Plot the initial line
plt.plot([0, 1], [f(0), f(1)], 'b-', label='f(x)')
plt.xlabel('$x$', fontsize=15)
plt.ylabel('$t$', fontsize=15)
plt.ylim([0,2])
plt.title('inputs (x) vs targets (t)')
plt.grid()
plt.legend(loc=2)
plt.show()
定义损失函数
y = w * x中的参数w,使得对于训练集中的N个样本,损失函数达到最小。
损失函数
即,我们的优化目标是:
损失函数优化目标
从函数中,我们可以发现,我们将所有样本的误差都进行了累加,这就是所谓的批训练(batch training)。我们也可以在训练的时候,每次训练一个样本,这种方法在在线训练中非常常用。
w的值是2。这个值就是我们函数f(x)的斜率。这个损失函数是一个凸函数,并且只有一个全局最小值。nn(x, w)函数实现了神经网络模型,cost(y, t)函数实现了损失函数。
# Define the neural network function y = x * w
def nn(x, w): return x*w
# Define the cost function
def cost(y, t): return ((t - y) ** 2).sum()
# Define the neural network function y = x * w
def nn(x, w): return x*w
# Define the cost function
def cost(y, t): return ((t - y) ** 2).sum()
cost vs weight
优化损失函数
对于教程中简单的损失函数,可能你看一眼就能知道最佳的权重是什么。但是对于复杂的或者更高维度的损失函数,这就是我们为什么要使用各种优化方法的原因了。
梯度下降
在训练神经网络中,梯度下降算法是一种比较常用的优化算法。梯度下降算法的原理是损失函数对于每个参数进行求导,并且利用负梯度对参数进行更新。权重w通过循环进行更新:
权重更新函数
w(k)表示权重w更新到第k步时的值,Δw为定义为:
权重的梯度
μ是学习率,它的含义是在参数更新的时候,每一步的跨度大小。∂ξ/∂w表示损失函数ξ对于w的梯度。对于每一个训练样本i,我们可以利用链式规则推导出对应的梯度,如下:
链式规则
ξi是第i个样本的损失函数,因此,∂ξi/∂yi可以这样进行推导:
函数推导
y(i) = x(i) ∗ w,所以我们对于∂yi/∂w可以这样进行推导:
函数推导
i个训练样本,Δw的完整推导如下:
Δw 完整推导
在批处理过程中,我们将所有的梯度都进行累加:
批处理函数推导
在进行梯度下降之前,我们需要对权重进行一个初始化,然后再使用梯度下降算法进行训练,最后直至算法收敛。学习率作为一个超参数,需要单独调试。
gradient(w, x, t)函数实现了梯度∂ξ/∂w,delta_w(w_k, x, t, learning_rate)函数实现了Δw。
# define the gradient function. Remember that y = nn(x, w) = x * w
def gradient(w, x, t):
return 2 * x * (nn(x, w) - t)
# define the update function delta w
def delta_w(w_k, x, t, learning_rate):
return learning_rate * gradient(w_k, x, t).sum()
# Set the initial weight parameter
w = 0.1
# Set the learning rate
learning_rate = 0.1
# Start performing the gradient descent updates, and print the weights and cost:
nb_of_iterations = 4 # number of gradient descent updates
w_cost = [(w, cost(nn(x, w), t))] # List to store the weight, costs values
for i in range(nb_of_iterations):
dw = delta_w(w, x, t, learning_rate) # Get the delta w update
w = w - dw # Update the current weight parameter
w_cost.append((w, cost(nn(x, w), t))) # Add weight, cost to list
# Print the final w, and cost
for i in range(0, len(w_cost)):
print('w({}): {:.4f} \t cost: {:.4f}'.format(i, w_cost[i][0], w_cost[i][1]))
# output
w(0): 0.1000 cost: 23.3917
w(1): 2.3556 cost: 1.0670
w(2): 2.0795 cost: 0.7324
w(3): 2.1133 cost: 0.7274
w(4): 2.1091 cost: 0.7273
2.0左右,接下来可视化一下梯度下降过程。
# Plot the first 2 gradient descent updates
plt.plot(ws, cost_ws, 'r-') # Plot the error curve
# Plot the updates
for i in range(0, len(w_cost)-2):
w1, c1 = w_cost[i]
w2, c2 = w_cost[i+1]
plt.plot(w1, c1, 'bo')
plt.plot([w1, w2],[c1, c2], 'b-')
plt.text(w1, c1+0.5, '$w({})$'.format(i))
# Show figure
plt.xlabel('$w$', fontsize=15)
plt.ylabel('$\\xi$', fontsize=15)
plt.title('Gradient descent updates plotted on cost function')
plt.grid()
plt.show()
Gradient descent updates plotted on cost function
梯度更新
k轮中w(k)的值。从图中我们可以得知,w的值越来越收敛于2.0。该模型训练10次就能收敛,如下图所示。
w = 0
# Start performing the gradient descent updates
nb_of_iterations = 10 # number of gradient descent updates
for i in range(nb_of_iterations):
dw = delta_w(w, x, t, learning_rate) # get the delta w update
w = w - dw # update the current weight parameter
# Plot the fitted line agains the target line
# Plot the target t versus the input x
plt.plot(x, t, 'o', label='t')
# Plot the initial line
plt.plot([0, 1], [f(0), f(1)], 'b-', label='f(x)')
# plot the fitted line
plt.plot([0, 1], [0*w, 1*w], 'r-', label='fitted line')
plt.xlabel('input x')
plt.ylabel('target t')
plt.ylim([0,2])
plt.title('input vs. target')
plt.grid()
plt.legend(loc=2)
plt.show()
input vs. target