美文网首页
人工智能00030 深度学习与图像识别书评30 神经网络基础12

人工智能00030 深度学习与图像识别书评30 神经网络基础12

作者: 良友知音 | 来源:发表于2023-12-06 07:45 被阅读0次

    上一个策略中,我们尝试的是随机权重,然后找到随机权重中最好的一批(Loss最小的那一批权重值)。

    其实不需要随机寻找权重,因为我们可以直接计算出最好的方向,也就是从数学上计算出最陡峭的方向。这个方向就是损失函数的梯度(gradient)。

    对一维函数进行求导,当函数包含多个参数的时候,我们称导数为偏导数。而梯度就是在每个维度上偏导数所形成的向量。

    计算梯度有两种方法:一种是缓慢的近似方法,即数值梯度法,其实现相对来说比较简单;另一种方法是分析梯度法,虽然其计算迅速,结果精确,但是实现时容易出错,且需要使用微分。

    本节主要针对数值梯度法进行讲解: 如果是对上述公式编写一  个Python函数的话,则可以写成:

    def eval_numerical_gradient(f,x):

       h = 0.00001

       return (f(x+h)-f(x))/h  

    上述实现中,该函数接受了两个参数,即“函数f”和参数x。 实践考量: 注意在数学公式中,h的取值是趋近于0的,然而在实际中,用一个很小的数值(比 如例子中的1e-5)来代替就足够了。

    在数值计算不出错的理想前提下,应尽可能地使用较小的h。还有,实际中使用中心差值公式(centered difference formula)效果较好。实现代码具体如下:

    def eval_numerical_gradient(f,x):

       h = 0.00001

       return (f(x+h)-f(x-h))/ (2*h)  

    1.梯度 之前的例子中,我们只计  算了一个变量的导数,如果存在多个x(比如:x0,x1,x2,x3,…,xn)的偏导数,则由全部变量的偏导数汇总而成的向量即为梯度(gradient),实现代码具体如下:

    def numerical_gradient(f,x):

       h = 0.00001

       grad = np.zeros_like(x)

       for idx in range(x.size):

           tmp_val = x[idx]

           #f(x+h)的计算

           x[idx] = tmp_val + h

    fxh1 = f(x)

           #f(x-h)的计算

           x[idx] = tmp_val - h

           fxh2 = f(x)

           grad[idx] = (fxh1 - fxh2) / (2*h)

           x[idx] = tmp_val

       return grad  

    函数numerical_gradient(f,x)中的实现看上去比较复杂,其实就是针对x中的每一个值都去做一下单个的eval_numerical_gradient运算  罢了。

    其中,np.zeros_like(x)会生成一个形状与x相同且所有元素都为0的数组。

    2.梯度下降法

    之前详细介绍过,这里只是稍微补充一下,虽然梯度的方向并不一定指向函数的最小值(可能存在局部最小值的可能性,因而没有找到全局最小值),但的确是在沿着它的方向尽可能地减少函数的值。

    下面我们利用Python语言来实现梯度下降法,实现代码具体如下:

    def gradient_descent(f,init_x,lr=0.01,step_num=100):

       x =init_x

       for i in range(step_num):

           grad = numerical_gradient(f,x)

           x -= lr*grad

       return x  

    其中,参数f是要进行最优化的函数,init_x是初始值,lr表示learning_rate(其是个超参数,需要自己调整),step_num代表梯度下降 法的重复数。

    3.神经网络的梯度下降法 神经网络的学习也要求梯度,这里的梯度所代表的是损失函数中关于权重以及偏移量(bias)的梯度。比如一个形状为2*2的权重为W的神经网络,损失函数用L表示,那么对于:

    其梯度表示为:    的元素由各个元素关于W的偏导数构成。对于每一个偏导数,其表示的意义是,当每个 W稍微变化的时候,损失函数L会发生多大的变化(这里的和W的形状是相同的。)

    4.补充概念:

    Np.nditer  import numpy as np

    arr1 = np.arange(0,30,5).reshape(2,3)

    it = np.nditer(arr1, flags=['multi_index'],op_flags=['readwrite'])

    while not it.finished:

       print(it.multi_index)

       it.iternext()

    输出如下:  (0, 0)

    (0, 1)

    (0, 2)

    (1, 0)

    (1, 1)

    (1, 2)  flags=['multi_index']

    表示对a进行多重索引,具体解释请看下面的示例代码。 上述代码段中参数的解释如下。 ·op_flags=['readwrite']表示  不仅可以对a进行read(读取),还可以write(写入),即相当于在创建这个迭代器的时候,我们就规定好了其具有哪些权限。

    ·print(it.multi_index)表示输出元素的索引,可以看到输出的结果都是index。

    ·it.iternext()表示进入下一次迭代,如果不加这一条语句的话,输出的结果就会一直都是(0,0)。

    5.讲了这么多的知识点,读   者可能会觉得对于一些概念还不是那么理解,所以本节打算再讲解一个小案例,将上述知识点做一个贯穿以帮助大家理解。

    案例的背景如下:输入一个X(人工识别这个X的图像为狗),让机器自动判断该图像的分类,其中,图像为三分类(类别分别为鸡、猫、狗),真实标签的分类为y=[0,0,1](标签已经转为one-hot类型,代表是狗)。

    假设我们有一个数据集X,X赋值为[[0.6,0.9]](已经将肉眼  识别的狗的图片转为了矩阵),从代码中能够看到X的形状为(1,2),代表的是1行2列:

    import numpy as np

    X = np.array([[0.6,0.9]])

    print(X.shape)  

    接着我们来定义一个简单的神经网络,在__init__初始化方法里,我们初始化了一个符合高斯分布的W矩阵,其大小为(2,3),并且为了保证效果的可靠性,我们设置了ran dom.seed(0)方法以保证每一次随机的W都是一致的;

    另外在Forward方法里,我们实现了前向传播;在Loss方法里,我们主要的实现思路是通过Forward方法得到预测值,经过Softmax方法转为相加之和为1的概率矩阵,之后再通过cross_entropy_error方法计算损失值Loss。

    现在大家都了解了我们的目的就是通过W的调整(利用梯度下降法)使得Loss值不断减少。详细代码如下:

    class simpleNet:

       def __init__(self):

           np.random.seed(0)

           self.W = np.random.randn(2,3)

       def forward(self,x):

           return np.dot(x,self.W)

       def loss(self,x,y):

           z = self.forward(x)

           p = _softmax(z)

           loss = cross_entropy_error(p,y)

    return loss  Softmax

    以及cross_entropy_error的实现方式分别在以后进行了详细讲解,如果读者有疑问请翻至对应章节阅读学习。

    接着,我们初始化一下简单神经网络,然后输出看下目前随机的W分别是哪些值,实现代码如下:

    net = simpleNet()

    print(net.W)

    X= np.array([[0.6,0.9]])

    p = net.predict(X)

    print('预测值为:',p)

    print('预测的类别为: ',np.argmax(p))  因为W是随机的,所以读者的输出结果与这里的输出结果可能会不一致,这里的输出结果仅供参考,我们可以发现,结果中预测类别是0对应的类别是鸡,很明显预测错误。

    [[ 1.76405235  0.40015721  0.97873798]

    [ 2.2408932   1.86755799

    -0.97727788]]

    预测值为: [[ 3.07523529  1.92089652 -0.2923073 ]]

    预测的类别为:  0  下面我们进一步看一下此时的损失值Loss:  y = np.array([0,0,1]) #输入正确类别

    print(net.loss(x,y))  计算一下损失值,我们会发现Loss非常大,其值为15.706416957363151,这个就需要基于数值微分的梯度下降法  。

    来进行优化了!详细代码如下:

    def numerical_gradient(f, x):

       h = 1e-4 # 0.0001

       grad = np.zeros_like(x)

       it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])

       while not it.finished:

           idx = it.multi_index

           tmp_val = x[idx]

           x[idx] = float(tmp_val) + h

           fxh1 = f(x) # f(x+h)

           x[idx] = tmp_val - h

           fxh2 = f(x) # f(x-h)

           grad[idx] = (fxh1 - fxh2) / (2*h)

           x[idx] = tmp_val #还原值

           it.iternext()

       return grad

    def gradient_descent(f,init_x,lr=0.01,step_num=1000):

       x =init_x

       for i in range(step_num):

    grad = numerical_gradient(f,x)

           x -= lr*grad

       return x

    f =  lambda w: net.loss(x,y)

    dw = gradient_descent(f,net.W)

    #需要更新的主要是W

    print(dw)  上述代码比较简单,而且之前陆陆续续已经讲解过了,这里就不再赘述了,我们此时输出dw观察下,通过梯度下降之后的dw的值具体如下:

    [[-4.09592461e-02 -8.44400784e-01  4.02830757e+00]

    [-4.66624189e-01  7.21000464e-04  3.59707650e+00]]  

    最后我们来验证下,通过计算出来的dw值,我们重新计算下损失值以及预测的类别,可以发现我们的损失值降低了,并且类别也预测正确了,具体代码如下:

    print('损失值变为: ',cross_entropy_error(_softmax (np.dot(x,dw)),y))

    print('预测类别为: ',np.argmax(np.dot(x,dw)))

    上述代码输出结果如下:  损失值变为:  0.06991990559251195

    预测类别为:  2  

    希望通过这个案例,读者对前文的知识点能有一个直观的了解。

    相关文章

      网友评论

          本文标题:人工智能00030 深度学习与图像识别书评30 神经网络基础12

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