CS231n:神经网络一例

经过对CS231n课程的学习,能够通过python实现神经网络,实现数据分类功能及分类器可视化,视为神经网络领域的入门一步。本文是CS231n课程《Putting it together: Minimal Neural Network Case Study》的简单描述与代码分析,有关神经网络的基础知识可以参考该课程的相关内容,这门课程清晰易懂,适合入门。

编译环境

Python 2.7 (Anaconda)
组件:numpy, matplotlib

# --coding:utf-8 -*-
import numpy as np
import matplotlib.pyplot as plt

第一步:生成数据

首先生成将要输入神经网络的数据。在该例子中数据维度为2,即单个数据包含2个特征(维度),对应x、y轴坐标,方便数据的可视化。生成的数据总共归属于3类,每类各包含100条数据,故生成数据这一步的输出X是一个300×2的矩阵(条目×维度)。

注:通常希望输入神经网络的数据每项特征均值为0,标准差为1
“Normally we would want to preprocess the dataset so that each feature has zero mean and unit standard deviation”
这意味着一般来说对于输入数据X,我们多半要进行以下两步

# 本例并未使用以下2步
X -= np.mean(X, axis = 0) # Make mean = 0
X /= np.std(X, axis = 0)  # Data normalization

在工程环境下,有时候数据维度(特征)特别大,如果只是应用全连接神经网络,我们甚至需要通过PCA(主成分分析)等方法,只提取最关键的特征(维度)进行计算,即有目的地缩小输入数据量。

本例出于简便起见巧妙地绕过了以上繁杂的过程~

# 1.Data generating
N = 100 # number of points per class
D = 2   # Dimentionality
K = 3   # numof classes
X = np.zeros((N*K, D))  # data matrix (row: single example)
y = np.zeros(N*K, dtype='uint8')  # class label
# 为确保数据均值为0并在[-1,1]之间,利用sin, cos的性质来创建数据(数据也较容易区分)
# randn服从标准正态分布
for j in xrange(K):
    ix = range(N*j, N*(j+1))
    r = np.linspace(0.0, 1, N)  # radius; linspace--在0~1之间等差的给出N个数
    t = np.linspace(j*4, (j+1)*4, N) + np.random.randn(N)*0.2 # theta, 4:近似pi?
    X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]
    y[ix] = j
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)  # c:散点颜色, s:散点大小, cmap:颜色表, cm.XXX对应的颜色表名
plt.show()

此时数据由于sin、cos函数的性质完美地保持在了[-1,1]之间,我们可以一心关注神经网络的实现了。生成的数据如下:

数据展示

第二步:初始化一个神经网络

1.确定全连接神经网络基本结构:

输入数据是二维的——Input Layer为二维
输出一个数据的预期类别号,而数据共包含三类——Output Layer为三维
我们希望神经网络有1个隐含层,包含100个神经元——Hidden Layer为100维

综上,我们希望构建的神经网络结构是这样的:

而神经网络的结构决定了神经网络的参数个数,即程序中关于神经网络的线性部分(wx+b)中w、b的总个数。反映在numpy中则是W、b、W2、b2矩阵的维度:

Input Layer
W:2×100, b:1×100
Hidden Layer
W2:100×3, b:1×3

这里采用随机生成的方法初始化各个权重W,偏置b初始为0:

# 2.Training Softmax Classifier
h = 100 # size of hidden layer
# initialize parameters randomly
W = 0.01 * np.random.randn(D,h)
b = np.zeros((1,h))
W2 = 0.01 * np.random.randn(h,K)
b2 = np.zeros((1,K))
2.确定激活函数、损失函数

神经元包含两个部分:线性部分即y=wx+b,和非线性部分f(y)。选用怎样的非线性部分,即人们常说的激活函数大可深究。损失函数的计算结果体现的是预测结果与真实结果间的差距大小,差距小了自然说明预测的更准确。这里我们选择ReLU函数作为激活函数,Softmax作为损失函数Loss。

3.为了反向传播——求导

同时由于神经网络在反向传导(BP)的过程中需要进行梯度计算,我们同样要求出激活函数、Loss的导数,根据链式求导法则完成BP。ReLU的导数显而易见,Softmax的求导也比较简单,过程如下:

以上计算将体现在反向传导的过程中,所以关于这里的理论需要有所了解。本文也是建立在了解神经网络运作理论基础上的。最后我们需要确定一些自定义参数,包括表示学习率的step_size,Full Softmax中的常数lambda:

step_size = 1e-0
reg = 1e-3 # lambda

三、训练神经网络

剩下的步骤就是模式化的神经网络前向、反向过程了:
前向传播:
线性部分计算y=wx+b
非线性部分计算z=max(0,y)
计算出该轮预测值scores(作为成果展示)
求该轮的偏差值loss,为后向传播服务
后向传播:
层层向前求导(梯度计算),利用梯度来更新神经网络中的参数,即神经网络的训练过程,让网络的功能向我们希望的结果靠拢

假设进行10000轮迭代(训练),代码如下:

num_examples = X.shape[0]
for i in xrange(10000):
    # 3.Compute the class scores
    hidden_layer = np.maximum(0, np.dot(X,W) + b)
    scores = np.dot(hidden_layer, W2) + b2

    # 4.Compute the SoftMax Loss
    exp_scores = np.exp(scores)  # unnormalized probability
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)  # normolize, probability of classes, 300 * 3
    # print probs

    correct_logprobs = -np.log(probs[range(num_examples),y]) # The Fomula Li
    # print correct_logprobs
    data_loss = np.sum(correct_logprobs) / num_examples

    reg_loss = 0.5 * reg * np.sum(W*W) # regularization loss
    # print reg_loss # very small
    loss = data_loss + reg_loss # The Formula L
    if i % 1000 == 0:
        print "Iteration %d: loss %f" % (i, loss) # loss ~= 1.1 = log(1.0/3),随机出现在各个类的probs约0.33
    # want loss = 0

    # 5.Compute the Gradiant
    dscores = probs # 300*3
    dscores[range(num_examples), y] -= 1  # d(softmax) = p-1 (j==k)
    dscores /= num_examples

    dW2 = np.dot(hidden_layer.T, dscores)
    db2 = np.sum(dscores,axis=0,keepdims=True)
    dhidden = np.dot(dscores, W2.T)
    dhidden[hidden_layer <= 0] = 0

    dW = np.dot(X.T, dhidden) # 链式求导:l_w = l_f * f_w, f=wx+b; W:2×3
    db = np.sum(dhidden, axis=0, keepdims=True) # b:1*3
    dW2 += reg*W2
    dW += reg*W # regularization gradient: (1/2)*reg*W*W_W == reg * W

    W += -step_size * dW
    b += -step_size * db
    W2 += -step_size * dW2
    b2 += -step_size * db2

经过训练,我们由最后一次的前向输出scores获取网络的最终预测结果,并借此计算预测精度

predicted_class = np.argmax(scores, axis=1) # most possible class predicted
print 'training accuracy: %.2f' % (np.mean(predicted_class == y))

至此,有关神经网络的训练过程到此结束。

四、结果可视化

为了将我们的运行成果直观的显示出来,我们利用matplotlib进行结果的可视化。由于本例样本是二维空间下的,基本思想就是穷取这一块儿的所有数据点,让它们经过神经网络的前向传播计算获得分类结果即类别号,利用类别号给不同的区域上色。matplotlib提供了等高线图的绘制,故实现如下:

# plot the resulting classifier
h = 0.02
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1  # 上下限
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
# arange按h间隔取数,meshgrid将数组复制成矩阵,形成xx,yy
# 相当于取这块儿而为区域内的所有点,作为实验数据
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
# ravel可改副本, c_按列(column)连接俩矩阵,作为输入数据X
# 然后模拟前向过程:两个wx+b的嵌套,Z为输出即三类各自的可能性中的最大者的序号,即predicted
# 最后通过reshape,由一维数组变xx维度
Z = np.dot(np.maximum(0, np.dot(np.c_[xx.ravel(), yy.ravel()], W) + b), W2) + b2
Z = np.argmax(Z, axis=1)
Z = Z.reshape(xx.shape)
fig = plt.figure()
# contourf 等值线图
plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral, alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)
plt.xlim(xx.min(), xx.max())
plt.ylim(yy.min(), yy.max())
plt.show()

分类结果:
分类结果

结语:本文面向的是有神经网络基础的Coder,对于神经网络的理论网络上的教程资源比比皆是,不乏浅显易懂者。CS231n也有详细教程,推荐想了解的人以此入手进行学习。

Portal:
CS231n Note
一个有关反向传播链式求导的超细致过程

转载请标明出处:http://blog.csdn.net/u011185952/article/details/54138891

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值