易懂的神经网络理论到实践(1):单个神经元+随机梯度下降学习逻辑与规则

AI韬鸽的笔记专栏中《零神经网络实战》系列持续更新介绍神经元怎么工作,最后使用python从0到1不调用神经网络框架(不使用tensorflow等框架)来实现神经网络。从0基础角度进行神经网络实战。 下一篇:Ai酱:零基础神经网络实战(2):理解并实现反向传播及验证神经网络是否正确 再下篇:Ai酱:适合初学者的神经网络理论到实践(3):打破概念束缚:强化学习是个啥?

目录为:

逻辑与、破除神经元认知障碍、手工调神经元、让计算机自己学习参数、随机梯度下降实践 作者: @Ai酱

逻辑与(AND)

我们先不管什么是神经元,咱们先介绍下逻辑与(AND)是什么。在计算机中,存储的是0和1。计算机有很多对这种01操作的运算,其中有一种运算叫做逻辑与,在编程中运算符通常表示为&。两个数进行逻辑与运算的规则就是只要有一个数是0,那么整个运算就是0。还是不太懂?我们举个例子。

1&0 == 0
0&1 == 0
0&0 == 0
1&1 == 1

然后我们要解决的问题是,怎么让神经元根据这四组数据自己学习到这种规则。接下来我们整理下这四个数据。然后我们将x1&x2==y的四种情况可以表示为如下所示。我们需要做的是让一个神经元根据这四个数据学习到逻辑与的规则。

x1 x2 y
1  0  0
0  1  0
0  0  0
1  1  1

也就是说,将(x1,x2)视作一个坐标,将它描点在二维平面是这样的。

我们让神经元能够学习到将(0,1)、(0,0)、(1,0)这些点分类为0,将(1,1)这个点分类为1。更直观的讲就是神经元得是像图中的这条直线一样,将四个点划分成两类。在直线左下是分类为0,直线右上分裂为1.

破除神经元的认知障碍

在人工智能范畴的的神经元它本质是一条直线,这条直线将数据划分为两类。与生物意义上的神经元可谓是千差万别。你可以理解为它是受到生物意义上的神经元启发,然后将它用来形象化数学公式。

既然神经元它在数学意义上就是一条直线那么,怎么表示呢?

学计算机一个很重要的思维就是:任何一个系统都是由输入、输出、和处理组成

我们在了解神经元也是一样,在本文中的需求是知道一个坐标(x1,x2),输出这个坐标的分类y。

我们按照计算机的输入输出思维整理下思路:

输入:x1,x2输出:y处理:自己学习到从x1,x2y的一种映射方法

我们知道输入输出,并且我们知道它是直线,那么我们就可以描述这个问题了。

我们让神经元能够学习到将(0,1)、(0,0)、(1,0)这些点分类为0,将(1,1)这个点分类为1。更直观的讲就是神经元得是像图中的这条直线一样,将四个点划分成两类。在直线左下是分类为0,直线右上分裂为1.

根据高中知识,一条直线将平面划分成两半,它可以用如下方式来描述所划分的两个半平面。

然后,前面提到过,神经元的形象化是受到生物启发。所以我们先介绍下怎么启发的。

生物意义上的神经元,有突触(输入),有轴突(输出),有细胞核(处理)。并且神经元传输的时候信号只有两个正离子和负离子。这和计算机1和0很类似。

总结下,神经元有突触(输入),有轴突(输出),有细胞核(处理)。开工画图:

好现在我们知道神经元的数学表达和怎么画它的形象化的图了。那么不如我们用编程也表达下它吧?

微信打开:从本质如何理解机器学习,https://gitbook.cn/gitchat/activity/5cd4d67b1fee452a3d37bf30

实践:用程序表示一个手工设置权重weight和偏置bias的神经元

去掉注释就10行左右的代码,好现在自己动手实践下吧?计算机思维:动手写代码进行实践是进步的唯一方法

# -*- coding: utf-8 -*-
"""
让神经元学习到逻辑与这个规则
@author: 李韬_varyshare
"""
class Neuron(object):
    def __init__(self):
        """
        手工设置神经元的权重w_i(输入x_i前面的系数),和偏置常数项b=-1.1
        """
        # 初始化权重数组,假定weights[i]和公式中的wi一一对应
        self.weights = [1.0,1.0]
        # 偏置也初始化为0
        self.bias = -1.1

    def f(self,x0,x1):
        """
        返回值是 weights[0]*x0+weights[1]*x1 + bias > 0? 1:0;
        计算这个用于判断(x0,x1)的分类。大于0则是点(x0,x1)在右上输出1,小于0则点在左下输出0;
        """
        if self.weights[0]*x0+self.weights[1]*x1 + self.bias > 0:
            return 1
        else:
            return 0

# 哈哈我们手工设置了一个神经元能完美实现逻辑与
n = Neuron()
# 0&1==0
print('0&1=',n.f(0,1))
# 1&0==0
print('1&0=',n.f(1,0))
# 0&0==0
print('0&0=',n.f(0,0))
# 1&1 == 1
print('1&1=',n.f(1,1))
"""
输出:
0&1= 0
1&0= 0
0&0= 0
1&1= 1
"""

那么怎么让计算机自己确定神经元的参数?

好了,可以洗洗睡散了。

但是,通常我们要解决的问题比这个复杂的多,神经网络是由神经元连接而成。而一般神经网络都是有几百个神经元。假设有200个神经元,我们现在一个神经元有3个参数,那么我们手动需要调200*3=600个参数。想想都觉得头皮发麻,而且200个神经元这还算少的,AlphaGo那种级别得上千个。恐怖如斯

梯度下降

可以看看这个回答初略理解梯度下降:什么是梯度下降法?

当然,数学家不会这么傻乎乎的自己手动调。那数学家怎么解决的呢?

x1 x2 g(x1,x2)
1  0     0
0  1     0
0  0     0
1  1     1

注意了:误差函数=代价函数=目标函数=损失函数,这三个词可以随意替换的。所以你们在其他地方看到这三个词就都替换成误差函数就可以。别被概念搞蒙了。一定记住误差函数(error)=代价函数(cost function)=目标函数(objective function)=损失函数(loss function)

那怎么求这个误差函数的最小值点呢?

数学家想到了一个办法叫做梯度下降。大家不知道这个方法没关系,现在你需要知道的是梯度下降可以让计算机自动求一个函数最小值时它的自变量值就可以了。注意梯度下降只关心自变量值。不关心函数值。这儿你迷糊也没关系,看到后面回头看这句话就懂了。反正梯度下降它做的事就是找自变量。

接下来咱们一起来学习下啥是梯度下降?啥是梯度?为何下降?梯度下降为了能求得最优参数?

是时候回答前面的“梯度下降”三问了。

  1. 啥是梯度?梯度就是导数,大家见到“梯度”就把它替换成导数就可以。在多维情况下梯度也是导数只不过是个向量,这个向量每个元素是一个偏导数。

  2. 为何下降?因为在数学里面(因为神经网络优化本来就是数学问题),在数学里面优化一般只求最小值点。那么有些问题要求最大值点怎么办?答:“在前面加负号”。简单粗暴就解决了。好继续回答为何下降,因为是求最小值点,那么这个函数肯定就像一口锅,我们要找的就是锅底的那个点在哪,然后我们当然要下降才能找到最小值点啦

  3. 为何梯度下降能求最优参数?接下来的几段,听我娓娓道来。因为只有知道梯度下降怎么做的,才知道为何它能求最优参数。所以接下来我要介绍的是,梯度下降如何实现利用负梯度进行下降的。

其实梯度下降就是让计算机这么猜,因为计算机判断速度快嘛。猜个上万次就能很容易猜到最优值。

回顾下我们怎么猜的?

总结规律:

现在总结下梯度下降(采用的是很常用的随机梯度下降)的步骤:

注意了,因为我们是二分类问题,虽然我们想让它直接输出0和1,但是很抱歉。它只能输出接近0和接近1的小数。为了方便编程,我们用-1代替0。也就是说只要神经元输出负数我们认为它就是输出0. 因为直接判断它是否接近0和是否接近1编程会更麻烦

注意了,因为我们是二分类问题,虽然我们想让它直接输出0和1,但是很抱歉。它只能输出接近0和接近1的小数。为了方便编程,我们用-1代替0。也就是说只要神经元输出负数我们认为它就是输出0. 因为直接判断它是否接近0和是否接近1编程会更麻烦

注意了,大家以后也会发现很多数据集二分类问题都会用1和-1而不用0.这是为了编程方便。我们只需要判断正负就可以知道是哪个分类。而不是需要判断是接近0还是接近1

注意我们只有下面这四个样本数据:

x1 x2 g(x1,x2)
1  0     -1
0  1     -1
0  0     -1
1  1     1
# 先随便猜w1,w2,b是多少
w1 = 0.5
w2= 0.8
b = 0.2
#我们设置一个学习率防止猜得太猛,比如跨度太大会从4猜到3,下一步就是从3猜到4
learning_rate = 0.01
do{
      for (x1,x2) in 样本集:
        # 我们先对各参数求下导数
        L关于w1的导数=2*(w1*x1+w2*x2+b-g(x1,x2))*x1
        L关于w2的导数=2*(w1*x1+w2*x2+b-g(x1,x2))*x2
        L关于b的导数=2*(w1*x1+w2*x2+b-g(x1,x2))
        # 接下来是猜各参数的环节
        # 下次猜的= 本次猜的 - 学习率*导数
        w1 = w1 - learning_rate*L关于w1的导数
        w2 = w2 - learning_rate*L关于w2的导数
        b = b - learning_rate*L关于b的导数
}while(足够多次数epoch);

# 这样我们就得到了最优的参数了
输出(w1,w2,b)

实践:动手实现随机梯度下降(根据上面的那个伪代码)

# -*- coding: utf-8 -*-
"""
让神经元学习到逻辑与这个规则
@author: 李韬_varyshare
"""
# 先随便猜w1,w2,b是多少
w1 = 0.666
w2 = 0.333
b = 0.233

def train():
    # 用于训练的数据(四行)一行样本数据格式为 [x1,x2,g(x1,x2)]
    data = [
            [1,  0,     -1],
            [0,  1,     -1],
            [0,  0,     -1],
            [1,  1,     1]
            ]
    global w1,w2,b # 告诉计算机我修改的是全局变量(每个函数都能修改这个变量)

    epoch = 20 # 同样的数据反复训练20次
    for _ in range(epoch):
        # 逐个样本更新权重
        for i in data:
            # 这里的i = [x1,x2,g(x1,x2)],它是data中的一行
            # 求各自导函数在(x1,x2,g(x1,x2))处的导函数值
            d_w1 = 2*(w1*i[0]+w2*i[1]+b-i[2])*i[0]
            d_w2 = 2*(w1*i[0]+w2*i[1]+b-i[2])*i[1]
            d_b = 2*(w1*i[0]+w2*i[1]+b-i[2])

            # 接下来就是愉快的理性猜环节了
            # 设置学习率,防止蹦的步子太大
            learning_rate = 0.01
            # 下次猜的数 = 本次猜的数 - 学习率*导数值
            w1_next = w1 - learning_rate*d_w1
            w2_next = w2 - learning_rate*d_w2
            b_next = b - learning_rate*d_b

            # 更新各参数
            w1 = w1_next
            w2 = w2_next
            b = b_next

            pass
        pass

def f(x1,x2):
    """
    这是一个神经元(本质就是一个表达式)
    经过训练,我们期望它的返回值是x1&x2
    返回值是 w1*x1+w2*x2 + b > 0? 1:0;
    计算这个用于判断(x0,x1)的分类。
    大于0则是点(x0,x1)在右上输出1,小于0则点在左下输出0;
    """
    global w1,w2,b # 告诉计算机我修改的是全局变量(每个函数都能修改这个变量)
    if w1*x1+w2*x2 + b > 0:
        return 1
    else:
        return 0

# 我们首先执行下训练,让神经元自己根据四条数据学习逻辑与的规则
train()
# 打印出模型计算出来的三个比较优的参数
print(w1,w2,b)
"""
输出:0.4514297388906616 0.2369025056182418 -0.611635769357402
"""

# 好我们测试下,看神经元有没有自己学习到逻辑与的规则
print("0&1",f(0,1))
print("1&0",f(1,0))
print("0&0",f(0,0))
print("1&1",f(1,1))
"""
输出:
0&1= 0
1&0= 0
0&0= 0
1&1= 1
"""

到这里我们已经完成了神经元自己学习逻辑与规则的实践了

github代码下载地址:https://github.com/varyshare/newbie_neural_network_practice/blob/master/neuron_gradient_descent.py

打开你的编辑器,参照我的思路实现吧

如果文章对你有帮助,不如点个赞,生活充满仪式感。你的赞是我持续更新的动力。

关注 @Ai酱

猜你喜欢:

Ai酱:pytorch神经网络实践(1): 安装与初次使用pytorch搭建神经网络实践手写数字识别教程

计算机生态圈是怎么样的?

有哪些事实没有一定计算机知识的人不会相信?

下一篇:Ai酱:零基础神经网络实战(2):理解并实现反向传播及验证神经网络是否正确

再下篇:Ai酱:适合初学者的神经网络理论到实践(3):打破概念束缚:强化学习是个啥?

Last updated