用代码一步步理解梯度下降和神经网络(ANN))

作者: nicktming | 来源:发表于2018-06-16 03:56 被阅读79次

    所有代码以及PPT:https://github.com/nicktming/code/tree/dev/machine_learning/Artificial_neural_network
    dev分支

    初了解

    ANN_10.jpeg

    这是一张典型神经网络的图,如果看不懂没关系,继续往下看.我们先从导数开始了解.

    导数

    该函数曲线在这一点上的切线斜率

    ANN_1.jpeg
    ann_11.jpeg
    有些函数在每个点的斜率都是一样的比如f(x)=3x,但是有些函数在每个点的函数可能都不一样比如f(x)=3x^2+4x+5.

    补充一下我个人对于导数的理解,几何含义是f(x)在点x的斜率,我理解为在点x的导数是在此处对f(x)的影响有多大.
    比如f(x)=10x 那么f'(x) = 10,意味着f(x)的变化是x变化的10倍, 比如f(1)=10;f(1.01)=10.1, x变化了0.01然而f(x)变化了10倍.

    复合函数的导数

    复合函数对自变量的导数,等于已知函数对中间变量的导数,乘以中间变量对自变量的导数.


    ann_12.jpeg

    偏导

    每个变量的导数


    ann_13.jpeg

    梯度

    ANN_1.jpeg
    ANN_2.jpeg
    例子1: f(x) = 3x^2 + 4x + 5

    代码 : gradient_test_1.py

    给出一个起点(start_x)和步长(step),如何利用梯度下降法寻找到可以使f(x)变小.

    导数: dx = f'(x) = 6x + 4; x := x - step * f'(x)

    import numpy as np 
    import matplotlib.pyplot as plt 
    
    # init some data 
    x_data = np.arange(-10, 11).reshape([21, 1])
    y_data = np.square(x_data)*3 + x_data * 4 + 5
    
    # for polt picture
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)
    ax.plot(x_data, y_data, lw=3)
    plt.ion()
    plt.show()
    
    start_x = 10
    step = 0.1
    current_x = start_x
    current_y = 3 * current_x * current_x + 4 * current_x + 5
    print("(loop_count, current_x, current_y)")
    for i in range(10):
        print(i, current_x, current_y)
        derivative_f_x = 6 * current_x + 4
        current_x = current_x - step * derivative_f_x
        current_y = 3 * current_x * current_x + 4 * current_x + 5
    
        ax.scatter(current_x, current_y)
        plt.pause(0.1)
    

    结果:

    ANN_3.jpeg
    ANN_4.jpeg

    结果可以看到(current_x)10一直在逼近f(x)的中轴线使得f(x)最小值.可以运行代码(需要安装python,numpy,matplotlib)进行查看,会有动画的效果,对于理解梯度下降会比较有帮助.

    例子2: f(x,y) = 3x^2 + 4y^2 + 5

    代码 : gradient_test_3.py

    相对于例子1,例子2是二维, 因此对x,y需要求偏导,意味对各个维度的斜线.

    给出一个起点(start_x, start_y) 和步长(step),如何利用梯度下降法去寻找到可以使f(x, y)变小?

    导数: dx = 6x dy = 8y

    import numpy as np 
    import matplotlib.pyplot as plt
    from mpl_toolkits.mplot3d import Axes3D
    
    # init some data 
    x_data = np.arange(-4, 4, 0.25)
    y_data = np.arange(-4, 4, 0.25)
    f_data = np.square(x_data)*3 + np.square(y_data) * 4 + 5
    X, Y = np.meshgrid(x_data, y_data)
    Z = np.sqrt(f_data)
    
    fig = plt.figure()
    ax = Axes3D(fig)
    ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='rainbow')
    plt.ion()
    plt.show()
    
    start_x = 10
    start_y = 10
    step = 0.01
    current_x = start_x
    current_y = start_y
    current_f = 3 * current_x * current_x + 4 * current_y + 5
    print("(loop_count, current_x, current_y, current_f)")
    for i in range(100):
        print(i, current_x, current_y, current_f)
        ### derivatives of x and y 
        derivative_f_x = 6 * current_x
        derivative_f_y = 8 * current_y
        ### update x, y
        current_x = current_x - step * derivative_f_x
        current_y = current_y - step * derivative_f_y
        ### current f 
        current_f = 3 * current_x * current_x + 4 * current_y + 5
    
    
        ax.scatter(np.meshgrid(current_x), np.meshgrid(current_y), np.sqrt(current_f))
        plt.pause(0.1)
    
    

    结果:

    ANN_5.jpeg
    ANN_6.jpeg

    可以看到f(x, y) = current_f在一直越来越小,对应的图就是下图,可以看到(x,y)一直在趋近于f(x, y)的最低点. (可以去运行代码,会有动画的效果,便于理解)

    计算图的导数

    代码:gradient_test_5.py

    ANN_7.jpeg
    ANN_8.jpeg

    目标:如何改变a,b,c使得J小于0.1?
    答案当然还是梯度下降法

    
    step = 0.1
    
    a = 5
    b = 3
    c = 2
    
    u = b * c 
    v = a + u
    J = 3 * v
    
    while not J < 0.1 :
        print("J:", J)
        # derivatives of variables
        derivative_J_v = v 
        derivative_v_a = 1
        derivative_v_u = 1
        derivative_u_b = c
        derivative_u_c = b 
    
        derivative_J_a = derivative_J_v * derivative_v_a
        derivative_J_b = derivative_J_v * derivative_v_u * derivative_u_b
        derivative_J_c = derivative_J_v * derivative_v_u * derivative_u_c
    
        #update variables
        a = a - step * derivative_J_a
        b = b - step * derivative_J_b
        c = c - step * derivative_J_c 
    
        u = b * c
        v = a + u
        J = 3 * v
    
    ANN_9.jpeg

    最终可以看到J已经小于0.1了,这个例子的展示有联合求导的情况,而且可以把a,b,c类比为神经网络中的参数.

    讨论Wx + b

    代码:gradient_test_6.py

    最简单的 f(x) = w1 * x + b1
    现在是坐标系中给出一些散点,然后希望用找到一条线可以最大程度拟合这些点.

    ANN_10.jpeg

    那如何才是最大程度拟合了这些点呢?我们可以设置我们自己的损失函数,在这里我们把cost_function = Least squares(最小平方法)当做损失函数,也就是所有点到这条线的距离和,这个距离和越小越好,那好了我们已经弄明白了也就是说如何改变w1,b1使得cost_function最小,是不是和上面的例子有点像,答案也是一样就是梯度下降法.

    首先就是需要求cost_functionw1,b1的导数.

    ANN_11.jpeg
    import numpy as np 
    import matplotlib.pyplot as plt 
    
    m = 20
    
    # init some data 
    x_data = np.arange(1, m + 1).reshape([m, 1])
    y_data = x_data*3 + 5
    
    # for polt picture
    fig = plt.figure()
    ax = fig.add_subplot(1,1,1)
    ax.scatter(x_data, y_data)
    plt.ion()
    plt.show()
    
    w1 = 0
    b1 = 0
    step = 0.01
    
    def cost_function(y_prediction):
        return 1.0/(2 * m) * np.sum(np.square(y_prediction - y_data))
    
    y_prediction = x_data * w1 + b1
    ax.plot(x_data, y_prediction, 'black', lw=3)
    
    print("(i, cost_function)")
    for i in range(250):
        
        print(i, cost_function(y_prediction))
    
        derivative_f_w1 = 1.0/m * np.sum(np.multiply(y_prediction - y_data, x_data))
        derivative_f_b1 = 1.0/m * np.sum(y_prediction - y_data)
    
        w1 = w1 - step * derivative_f_w1
        b1 = b1 - step * derivative_f_b1
        y_prediction = x_data * w1 + b1
    
    
        try:
            ax.lines.remove(lines[0])
        except Exception:
            pass
    
        lines = ax.plot(x_data, y_prediction, 'r-', lw=3)
        plt.pause(0.1)
    
        
    
    print('w1:', w1, 'b1:', b1)
    

    结果:

    image.png
    image.png
    ANN_12.jpeg

    可以看到cost_function越来越小,黑线是初始w1=0,b1=0是线的位置,最终生成的红线是最终的线(w1=3.19,b1=2.32). (可以运行代码,代码会有线是如何模拟从初始位置到最终位置的)

    外层再加入一个Sigmoid函数
    image.png

    h'(x) = h(x) * (1 - h(x))

    image.png

    目标还是改变w1,b1使cost_function变小,所以还是求cost_function关于w1,b1的偏导,加了一个函数,利用联合求导的方式还是可以求出答案.

    image.png

    代码没有写了,感兴趣的人可以自己写一下哈(一样的道理)

    激励函数

    一般都是非线性函数

    image.png
    为什么需要非线性函数?

    如果是线性激励函数的话,整个网络还是线性方程.可以表示成:
    (参数)x1 + (参数)x2 + … + (参数)xn

    ANN: Artificial neural network

    image.png
    image.png
    例子1: num_of_samples=1

    代码:gradient_test_2.py

    cost_function为最小平方法

    image.png
    image.png

    目标还是一致,希望可以通过改变W11,W12,W21,W22,W31,W32来使cost_function越来越小,因此需要求出cost_function关于这些参数的偏导.

    image.png
    image.png
    import numpy as np
    
    m = 10
    step = 0.01
    
    def sigmoid(x):
        return 1/(1+np.exp(-x))
    
    def derivative_sigmoid(x):
        return np.multiply(1 - sigmoid(x), sigmoid(x))
    
    def cost_function(yo, Y):
        return 1./(2*m) * np.sum(np.square(np.subtract(yo, Y)))
    
    #shape 1*3
    X = np.ones((m, 3))
    Y = np.random.rand(m, 2)
    
    #shape 3*2
    W = np.ones((3, 2))
    
    #shape 1*2
    y = np.dot(X, W)
    
    #shape 1*2
    yo = sigmoid(y)
    
    cost = cost_function(yo, Y)
    
    print("start:", cost)
    
    cnt = 0;
    
    while not cost < 0.1 :
        derivative_c_y = np.subtract(yo, Y) / m
    
        derivative_yo_y = derivative_sigmoid(y)
    
        dw = np.dot(X.T, np.multiply(derivative_c_y, derivative_yo_y))
    
        W = W - step * dw
        y = np.dot(X, W)
        yo = sigmoid(y)
        cost = cost_function(yo, Y)
        cnt += 1
    
    print("end:", cost)
    print("cnt:", cnt)
    

    结果:


    image.png
    例子2:num_of_samples=m

    其实中间过程还是一样,需要求各个参数的偏导.我只是想说明一下当num_of_samples>1时,代码还是一样.

    image.png

    tensorflow实现一个简单的神经网络

    import tensorflow as tf
    import numpy as np
    
    x_data = np.float32(np.random.rand(2, 100))
    y_data = np.dot([0.100, 0.200], x_data) + 0.300
     
    b = tf.Variable(tf.zeros([1]))
    W = tf.Variable(tf.random_uniform([1, 2], -1.0, 1.0))
    y = tf.matmul(W, x_data) + b
    
    
    loss = tf.reduce_mean(tf.square(y - y_data))
    optimizer = tf.train.GradientDescentOptimizer(0.5)
    train = optimizer.minimize(loss)
    
    
    init = tf.initialize_all_variables()
    
    
    sess = tf.Session()
    sess.run(init)
    
    for step in xrange(0, 201):
        sess.run(train)
        if step % 20 == 0:
            print step, sess.run(W), sess.run(b)
    

    下一篇将会用代码进一步分析多层是如何传递错误的,也就是用代码理解反向
    传播,通过代码理解反向传播(backpropagation)

    参考

    1.网易云课堂: 深度学习-吴恩达
    2.博客:https://www.jianshu.com/p/c7e642877b0e
    3.tensorflow官网

    相关文章

      网友评论

        本文标题:用代码一步步理解梯度下降和神经网络(ANN))

        本文链接:https://www.haomeiwen.com/subject/wxizsftx.html