写一个会学习的神经元


介绍

周末看了个视频,说是人的神经元怎么怎么传脉冲就能进行思考,我不知道这是真的还是假的,但听起来有些道理。这让我想到了之前很火的人工智能,突然觉得理解了点什么,于是就弄了段看起来能学习的代码。

image

这段程序的功能很简单,就是模仿一个拥有四个「树突」中的神经元,它会对「树突」上的各个输入进行抑制或放大,然后合起来得到一个值,如果合起来的值大于某个阈值,它的「突触」就输出1(激活),否则输出0(继续睡觉)。

image

它的样子大概如此,是不是很像(抽象)一个神经元?

我们思路是这样的:

  • 先用代码实现这个神经元

  • 然后呢,就开始给这个神经元一些学习资料,当它学习得足够刻苦后,就会变得聪明起来,成为一个Smart Boy,也就是SB~。 为了简单起见,学习资料就是4个0/1构成的列表,例如(1,0,1,0),答案就是1或0。

  • 接着,我们就开始对它进行一些测试,看它是不是真的学会了。也就是给它一些(1, 0, 1, 0)看它能不能按学习资料中的规律给出0或1的答案。

实现

首先是实现神经元,我们使用四个参数Wi来表现树突对上面输入的抑制或放大作用。这几个参数分别作用在input的各个分量上

class SmartBoy:

    def __init__(self):
        self.w1 = 1
        self.w2 = 1
        self.w3 = 1
        self.w4 = 1

    def brain(self, input):
        r = self.w1*input[0] + self.w2*input[1] + self.w3*input[2] + self.w4*input[3]
        if r > 0.5:
            return 1
        else:
            return 0

按现在这样,这个神经元只会把四个输入全部加起来,然后看是不是大于0.5,决定是激活(1)或是继续睡觉(0)。这不够智能,我们还需要它能学习文化知识,改变自己的认知。

于是加入这样一串规则,就是给个exercise自己想,然后看答案,偏大就随机把各个W缩小,不然就放大。这样学一阵子之后,它对各个树突的态度就变化了,神经元也就成长了,它懂得了应该接受哪个树突,又该拒绝哪个。

    def learn(self, exer, key):
        ans = self.brain(exer)
        if ans == key:
            return
        else:
            if ans > key:
                self.w1 = self.w1 * random.random()
                self.w2 = self.w2 * random.random()
                self.w3 = self.w3 * random.random()
                self.w4 = self.w4 * random.random()
            else:
                self.w1 = self.w1 * random.random() + 1
                self.w2 = self.w2 * random.random() + 1
                self.w3 = self.w3 * random.random() + 1
                self.w4 = self.w4 * random.random() + 1

现在,我们需要准备一本好的教材,一本告诉神经元exer开头是1,就应该输出1的教材

    book = [
        [(1,1,0,0), 1],
        [(1,1,0,1), 1],
        [(1,0,1,0), 1],
        [(0,0,1,1), 0],
        [(1,0,1,1), 1],
        [(1,1,0,1), 1],
        [(1,1,1,1), 1],
        [(0,1,1,1), 0],
        [(1,1,1,0), 1],
        [(0,1,1,0), 0],
        [(1,1,0,1), 1],
        [(1,0,1,1), 1],
    ]

现在我们召唤一个神经元sb,让它开始学习我们的教材300遍,读书三百遍,其义自现。

    sb = SmartBoy()

    for i in range(300):
        for exer, key in book:
            sb.learn(exer, key)

接着我们出一套试题

    tests = [
        (1,0,1,1),
        (1,1,1,1),
        (0,0,1,1),
        (1,0,1,0),
        (1,0,0,0),
        (1,1,0,0),
        (1,1,1,0),
        (0,0,1,0),
        (0,0,0,0),
        (1,1,0,1),
    ]

考试开始

    for test in tests:
        ans = sb.brain(test)
        print(test, ans)

    print(f"brain is {sb.w1} {sb.w2} {sb.w3} {sb.w4}")

那么,它给出的答案是什么呢?(exer开头是1,就应该输出1)

(1, 0, 1, 1) 1
(1, 1, 1, 1) 1
(0, 0, 1, 1) 0
(1, 0, 1, 0) 1
(1, 0, 0, 0) 1
(1, 1, 0, 0) 1
(1, 1, 1, 0) 1
(0, 0, 1, 0) 0
(0, 0, 0, 0) 0
(1, 1, 0, 1) 1
brain is 0.6200796171156872 0.11142487074770839 0.17477170033243886 0.002014989461413244

没错,全对了

换本教材

既然说了这个神经元可以学习,那么我们给它换本教材再学300遍,应该就能学会新的东西了。那到底是不是呢,我们试试看。

现在给出一本新书,表达的规律是,中间是两个1就给出1

    book = [
        [(1,1,0,0), 0],
        [(1,1,0,1), 0],
        [(1,0,1,0), 0],
        [(0,0,1,1), 0],
        [(1,0,1,1), 0],
        [(1,1,0,1), 0],
        [(1,1,1,1), 1],
        [(0,1,1,1), 1],
        [(1,1,1,0), 1],
        [(0,1,1,0), 1],
        [(1,1,0,1), 0],
        [(1,0,1,1), 0],
    ]

依然学习300遍,然后用之前的试题来测测看,它给的答案如下

(1, 0, 1, 1) 0
(1, 1, 1, 1) 1
(0, 0, 1, 1) 0
(1, 0, 1, 0) 0
(1, 0, 0, 0) 0
(1, 1, 0, 0) 0
(1, 1, 1, 0) 1
(0, 0, 1, 0) 0
(0, 0, 0, 0) 0
(1, 1, 0, 1) 0
brain is 0.08391736481954896 0.211282837293934 0.31411813879982625 0.033044625275163504

没错,它学会了!

再换一本

这次我们的教材是,末尾的1就输出1

    book = [
        [(1,1,0,0), 0],
        [(1,1,0,1), 1],
        [(1,0,1,0), 0],
        [(0,0,1,1), 1],
        [(1,0,1,1), 1],
        [(1,1,0,1), 1],
        [(1,1,1,1), 1],
        [(0,1,1,1), 1],
        [(1,1,1,0), 0],
        [(0,1,1,0), 0],
        [(1,1,0,1), 1],
        [(1,0,1,1), 1],
    ]

测试结果如下

(1, 0, 1, 1) 1
(1, 1, 1, 1) 1
(0, 0, 1, 1) 1
(1, 0, 1, 0) 0
(1, 0, 0, 0) 0
(1, 1, 0, 0) 0
(1, 1, 1, 0) 0
(0, 0, 1, 0) 0
(0, 0, 0, 0) 0
(1, 1, 0, 1) 1
brain is 0.05803250548287294 0.030302330140830957 0.09168660880948953 0.5004478665614163

没错!sb它又学会了!

它很行吗

从上面可以看出,只要我们给它一本新的教材,然后学个300遍,它就能掌握其中的规律,并顺利通过测试。

但是,当教材不够好时,它还是学不会的。例如,教材给出的例子太少,或者,教程给出的例子不够典型,它的学习效果就会很差。 教材真的很重要。

或者,需要学习的规律太复杂时,它也是学不会的,毕竟,它太简单了,也就四个树突。

当然,由于我们使用的是随机数,所以有时候即使学习300遍,也还是学不会,这时就可以重造了。

如果我们实现更多的神经元,那这一堆神经元应该能实现更复杂的功能。

完整的代码

import random


class SmartBoy:

    def __init__(self):
        self.w1 = 1
        self.w2 = 1
        self.w3 = 1
        self.w4 = 1

    def brain(self, input):
        r = self.w1*input[0] + self.w2*input[1] + self.w3*input[2] + self.w4*input[3]
        if r > 0.5:
            return 1
        else:
            return 0

    def learn(self, exer, key):
        ans = self.brain(exer)
        if ans == key:
            return
        else:
            if ans > key:
                self.w1 = self.w1 * random.random()
                self.w2 = self.w2 * random.random()
                self.w3 = self.w3 * random.random()
                self.w4 = self.w4 * random.random()
            else:
                self.w1 = self.w1 * random.random() + 1
                self.w2 = self.w2 * random.random() + 1
                self.w3 = self.w3 * random.random() + 1
                self.w4 = self.w4 * random.random() + 1

if __name__=="__main__":
    book = [
        [(1,1,0,0), 0],
        [(1,1,0,1), 1],
        [(1,0,1,0), 0],
        [(0,0,1,1), 1],
        [(1,0,1,1), 1],
        [(1,1,0,1), 1],
        [(1,1,1,1), 1],
        [(0,1,1,1), 1],
        [(1,1,1,0), 0],
        [(0,1,1,0), 0],
        [(1,1,0,1), 1],
        [(1,0,1,1), 1],
    ]

    tests = [
        (1,0,1,1),
        (1,1,1,1),
        (0,0,1,1),
        (1,0,1,0),
        (1,0,0,0),
        (1,1,0,0),
        (1,1,1,0),
        (0,0,1,0),
        (0,0,0,0),
        (1,1,0,1),
    ]

    sb = SmartBoy()

    for i in range(300):
        for exer, key in book:
            sb.learn(exer, key)

    for test in tests:
        ans = sb.brain(test)
        print(test, ans)

    print(f"brain is {sb.w1} {sb.w2} {sb.w3} {sb.w4}")