人工神经网络初步

参考博客:https://blog.csdn.net/han_xiaoyang

人工神经网络其实在很久以前就被提出来了,苦于当时没有足够强大的硬件(比如现在也有点烂大街的GPU)去支撑理论的实践(其实还有一部分原因是面对图像这样的高维数据,最初的神经网络确实没有做卷积神经网络这样特殊的处理,从而参数极大),因此消停了非常长的时间。当然,它的热度在近两年被推到了一个近乎极端的状态,似乎是个好点的实验室,是个大型点的互联网公司,不说自己会点深度学习和神经网络都不好意思说自己是这个圈子里混的。当然,媒体的炒作是一方面啦,但是神经网络确实在近期的一些任务中表现出了非常强势的优势。比如在很多分类问题中,样本是不可线性切分的,那特征的处理就尤为重要了,有意思的是,这正是神经网络所擅长的,在每一次的前向计算过程中,就在自动地做feature mapping,而到达softmax层的时候,其实构造出来的feature空间里,样本已经是能近似线性切分的了,于是三下五除二把其他分类器远远甩在后面了。背景废话了一大堆,咱们来具体看看,简单的人工神经网络是怎么样对样本点做非线性切分的。

import numpy as np
from sklearn.datasets import make_moons
import matplotlib.pyplot as plt
from sklearn.linear_model import LogisticRegressionCV

1、手动生成一个随机的平面点分布,并画出来图形

np.random.seed(0)
X, y = make_moons(200, noise=0.20)#符合高斯0.2分布
# X是一个200*2的矩阵,都符合高斯0.2分布,例如0.743461176196,0.464656328377
# y是200*1的矩阵,值都是0或者1,表示数据的标签
#plt.scatter(X[:,0], X[:,1], s=40, c=y, cmap=plt.cm.Spectral)
plt.scatter(X[:,0], X[:,1], s=40, c=y)
plt.show()

2、定义一个函数用来画分类结果的决策边界(也就是分界线)

先顶一个一个函数来画决策边界

def plot_decision_boundary(pred_func):
   # 2.1设定最大最小值,附加一点点边缘填充
    x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
    #x_min值为-1.85462041177,x_max值为2.75536302013
    y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
    #y_min的值为-1.43206382316,y_max的值为1.84161.44582
    h = 0.01
    # 2.2通过meshgrid生成网格点
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
    #xx是一个(328,461)的矩阵,size为151208,以0.01为差值
    #yy是一个(328,461)的矩阵,size为151208,以0.01为差值
    # 2.3用预测函数预测一下
    Z = pred_func(np.c_[xx.ravel(), yy.ravel()])#Z的值为151208个0或者1的数
    #上面这句话的调用和plot_decision_boundary(lambda x: clf.predict(x))
    #H=clf.predict(np.c_[xx.ravel(), yy.ravel()])
    #np.c_按照列进行合并,np.r_按照行进行合并元素
    """
     lambda x: clf.predict(x)是什么?
     (1)lambda实际上是一种函数,当你想运行一个函数而又毫不关心他的函数名时,
     就可以叫他lambda。这个函数实际上可以写为
    def call_clf_predict(x):
        return clf.predict(x)
    (2)plot_decision_boundary(call_clf_predict)
      lambda函数实际上和lambda演算相关,lambda演算就是尝试把函数当做数去使用。
     第8行到底接受到了什么?
     答案是第8行实际上接收到了一个方法(函数),这个方法被用pred_func进行了替换。
     那么这个方法又是谁呢?答案是lambda函数,lambda函数又是谁呢?答案是clf.predict(x)。
     所以pred_func实际上就是clf.predict(x)。因此在调用环节,plot_decision_boundary(lambda x: clf.predict(x)) 
     等价于 plot_decision_boundary(clf.predict)
    """
    Z = Z.reshape(xx.shape)
    # 2.4画出图测试例子
    plt.contourf(xx, yy, Z, cmap=plt.cm.Spectral)
    plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Spectral) 

3、先用传统的逻辑回归来做一下分类,并画出判定边界

(1)在scikit-learn中,与逻辑回归有关的主要是这3个类。LogisticRegression, LogisticRegressionCV 和logistic_regression_path。
其中LogisticRegression和LogisticRegressionCV的主要区别是,LogisticRegressionCV使用了交叉验证来选择正则化系数C,而LogisticRegression需要自己每次指定一个正则化系数。除了交叉验证以及选择正则化系数C以外, LogisticRegression和LogisticRegressionCV的使用方法基本相同。
(2)logistic_regression_path类则比较特殊,主要是用在模型选择的时候,它拟合数据后,不能直接来做预测,只能为拟合数据选择合适逻辑回归的系数和正则化系数。一般情况用不到这个类,所以后面不再讲述logistic_regression_path类。
(3)此外,scikit-learn里面有个容易让人误解的类RandomizedLogisticRegression,虽然名字里有逻辑回归的词,但是主要是用L1正则化的逻辑回归来做特征选择的,属于维度规约的算法类,不属于我们常说的分类算法的范畴

3.1 先来瞄一眼逻辑斯特回归对于它的分类效果

clf = LogisticRegressionCV()
clf.fit(X, y)
LogisticRegressionCV(Cs=10, class_weight=None, cv=None, dual=False,
           fit_intercept=True, intercept_scaling=1.0, max_iter=100,
           multi_class='ovr', n_jobs=1, penalty='l2', random_state=None,
           refit=True, scoring=None, solver='lbfgs', tol=0.0001, verbose=0)

3.2画一下决策边界

print("clf.predict(x):",clf.predict(X))
plot_decision_boundary(lambda x: clf.predict(x))
plt.title("Logistic Regression")
plt.show()
clf.predict(x): [0 1 0 1 1 1 1 1 1 1 0 1 1 1 0 1 0 1 0 0 1 1 0 1 0 0 1 1 1 0 0 0 1 0 0 1 1
 1 0 0 1 0 0 1 1 0 1 0 1 1 0 1 1 0 1 0 0 1 0 0 1 0 1 0 1 0 0 1 0 0 1 0 1 0
 1 0 1 1 0 1 1 0 1 1 0 0 1 0 1 1 0 0 1 0 1 1 1 1 0 1 1 1 0 1 0 0 0 0 1 0 0
 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 1 1 1 1 0 0 0 1 1 1 1 0 1 0 0 0 0 0 0 1 0 1
 0 1 1 1 1 0 1 0 1 1 0 0 1 1 0 1 1 1 0 1 1 1 0 0 0 0 1 1 0 0 0 0 1 0 1 1 1
 0 0 1 0 0 0 0 1 1 0 0 1 1 0 1]

4、试一个简单的人工神经网络

这是一个分类问题,因此我们用softmax分类器(LR的多分类),并且用互熵损失作为损失函数

BP计算梯度以便使用(随机)梯度下降

num_examples = len(X) # 样本数
nn_input_dim = 2 # 输入的维度
nn_output_dim = 2 # 输出的类别个数
#梯度下降参数
epsilon = 0.01 # 学习率
reg_lambda = 0.01 # 正则化参数

4.1定义损失函数

先定义了正向推导过程,用互熵损失作为损失函数

def calculate_loss(model):
    W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
    #(1)前向传播计算预测值
    z1 = X.dot(W1) + b1#正向推
    a1 = np.tanh(z1)
    z2 = a1.dot(W2) + b2
    exp_scores = np.exp(z2)
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    #(2)计算损失
    corect_logprobs = -np.log(probs[range(num_examples), y])
    data_loss = np.sum(corect_logprobs)
    #(3)在损失函数中添加正则化项W1^2+W2^2
    data_loss += reg_lambda/2 * (np.sum(np.square(W1)) + np.sum(np.square(W2)))
    return 1./num_examples * data_loss

4.2完整的训练建模函数定义

def build_model(nn_hdim, num_passes=20000, print_loss=False):
    '''
    参数:
    1) nn_hdim: 隐层节点个数
    2)num_passes: 梯度下降迭代次数,经过测试50000次和20000区别不大
    因为到12000后,Loss趋向0.071316,不再向下减小
    3)print_loss: 设定为True的话,每1000次迭代输出一次loss的当前值
    '''
    # (1)随机初始化一下权重
    np.random.seed(0)
    #nn_input_dim值为2,nn_output_dim值为2,nn_hdim值为3
    W1 = np.random.randn(nn_input_dim, nn_hdim) / np.sqrt(nn_input_dim)
    b1 = np.zeros((1, nn_hdim))
    W2 = np.random.randn(nn_hdim, nn_output_dim) / np.sqrt(nn_hdim)
    b2 = np.zeros((1, nn_output_dim))
    # 这是咱们最后学到的模型
    model = {}
    # (2)开始梯度下降...
    for i in range(0, num_passes):
        #(3)前向传播计算loss
        z1 = X.dot(W1) + b1#X为200*2,W1为2*3,z1=X.dot(W1)值为200*3,再加上b1(1*3都为0)的值
        a1 = np.tanh(z1)#用非线性函数tanh激活一下,a1为200*3
        z2 = a1.dot(W2) + b2#a1为200*3,W2为3*2,a1.dot(W2)值为200*2,再加上b2(1*3都为0)的值
        exp_scores = np.exp(z2)#z2为200*2的矩阵,后两部是softmax的处理过程,都变为概率
        probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
        # (4)反向传播看课件上的已有的公式推导
        delta3 = probs#probs是一个200*2的矩阵,里面数据都为softmax的概率值
        delta3[range(num_examples), y] -= 1#这句话不太明白
        dW2 = (a1.T).dot(delta3)#a1为200*3的矩阵,a1.T为3*200,delta3为200*2,结果为3*2
        db2 = np.sum(delta3, axis=0, keepdims=True)#db2为1*2矩阵
        delta2 = delta3.dot(W2.T) * (1 - np.power(a1, 2))
        #delta3为200*2,W2为3*2,W2.T为2*3,delta3.dot(W2.T)结果为200*3
        #a1为200*3,1-np.power(a1,2)为200*3,
        #矩阵*矩阵的结果是对应元素相乘,结果delta2仍为200*3
        dW1 = np.dot(X.T, delta2)
        db1 = np.sum(delta2, axis=0)
        #(5)加上正则化项
        dW2 += reg_lambda * W2
        dW1 += reg_lambda * W1
        #(6)梯度下降更新参数
        W1 += -epsilon * dW1#epsilon值为0.01
        b1 += -epsilon * db1
        W2 += -epsilon * dW2
        b2 += -epsilon * db2
        #(7)重新更新W1,b1,W2和b2的值
        model = { 'W1': W1, 'b1': b1, 'W2': W2, 'b2': b2}
        #(8)每迭代1000次输出一下
        if print_loss and i % 1000 == 0:
           print("Loss after iteration %i: %f" %(i, calculate_loss(model)))
    return model

4.3判定结果的函数

def predict(model, x):
    W1, b1, W2, b2 = model['W1'], model['b1'], model['W2'], model['b2']
    #(1)前向运算
    z1 = x.dot(W1) + b1
    a1 = np.tanh(z1)
    z2 = a1.dot(W2) + b2
    exp_scores = np.exp(z2)
    #5.2计算概率输出最大概率对应的类别
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    return np.argmax(probs, axis=1)#返回最大的probs

5、建立3个隐层的神经网络

model = build_model(3, print_loss=True)
Loss after iteration 0: 0.432387
Loss after iteration 1000: 0.068947
Loss after iteration 2000: 0.069541
Loss after iteration 3000: 0.071218
Loss after iteration 4000: 0.071253
Loss after iteration 5000: 0.071278
Loss after iteration 6000: 0.071293
Loss after iteration 7000: 0.071303
Loss after iteration 8000: 0.071308
Loss after iteration 9000: 0.071312
Loss after iteration 10000: 0.071314
Loss after iteration 11000: 0.071315
Loss after iteration 12000: 0.071315
Loss after iteration 13000: 0.071316
Loss after iteration 14000: 0.071316
Loss after iteration 15000: 0.071316
Loss after iteration 16000: 0.071316
Loss after iteration 17000: 0.071316
Loss after iteration 18000: 0.071316
Loss after iteration 19000: 0.071316

6、然后再把决策/判定边界画出来并输出

plot_decision_boundary(lambda x: predict(model, x))
plt.title("Decision Boundary for hidden layer size 3")
plt.show()

7、测试不同的隐层个数对结果的影响

plt.figure(figsize=(16, 32))
hidden_layer_dimensions = [1, 2, 3, 4, 5, 20, 50]
for i,nn_hdim in enumerate(hidden_layer_dimensions):
    plt.subplot(5, 2, i+1)
    plt.title('Hidden Layer size %d' % nn_hdim)
    model = build_model(nn_hdim)
    plot_decision_boundary(lambda x: predict(model, x))
    plt.show()
    
<matplotlib.figure.Figure at 0x1354044e908>

猜你喜欢

转载自blog.csdn.net/CSU_GUO_LIANG/article/details/80498003