# 《深度学习入门》的cpp实现-ch02: 感知机

## 定义
感知机(perceptron)是接受**多个**输入信号，输出**一个**信号的的算法。

以一个接受两个输入信号的感知机为例子

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1659876137836/JJln9UFjb.png align="left")

神经元接受两个输入值(x1, x2), 在不同的输入上具有不同的权重(w1, w2),  神经元会处理输入计算各路总和`(w1*x1 + w2*x2)`，当值高于某个阈值(bias)的时候，神经元被激活。

以数学公式表达就是

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1659876323672/FpQPa4UC1.png align="left")

## 实现

可以使用单层感知机实现`AND`, `NAND`, `OR`的门逻辑，只需要调整不同的 `w1`, `w2`和 `bias`.

`w1`、`w2`称为**权重**, 是控制着输入信号**重要性**的参数。`bias`称为`偏置`, 是调整神经元**被激活的容易程度**的参数。

```cpp
bool perceptron(int a, int b, double x1, double x2, double bias) {
    Eigen::Matrix<double, 1, 2> input;
    input << a, b;
    Eigen::Vector2d weight(x1, x2);
//    std::cout << input * weight << std::endl;
    return input.dot(weight) + bias > 0;
}

// [w1, w2] = [0.5, 0.5] bias = -0.7
bool and_neural(int a, int b) {
    return perceptron(a, b, 0.5, 0.5, -0.7);
}

bool nand_neural(int a, int b) {
    return perceptron(a, b, -0.5, -0.5, 0.7);
}

bool or_neural(int a, int b) {
    return perceptron(a, b, 0.5, 0.5, -0.2);
}
```

需要注意的是，在这里决定`w1`, `w2` 和 `bias` 具体值的是人。而机器学习就是将这个决定参数值的过程，交给计算机自动进行。**学习**是指确定合适参数的过程，而人需要做的事思考整个感知集的构造(模型)，并将数据集交给计算机，进行学习。

## 感知机的局限性

使用单层感知机无法实现 `XOR` 门，这是因为对于或门的逻辑，可以如下图表示

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1659877511708/Y0Hn8H9fs.png align="left")

用一条直线将整个空间分为两部分，在一个空间输出1，在另一个空间输出0


![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1659877576617/cC1XYBs8c.png align="left")

而对于`XOR`门, 无法使用一条直线分割整个空间。

因此，感知机的局限在于它只能表示一条由直线分割的空间(对于两个输入的感知机，是一条直线；有三个输入的感知机是一个平面； 有n个输入的感知机，是一个n-1维度的“平面”)，无法表示弯曲的曲线。 

由曲线分割的空间称为`非线性空间`，由直线分割的空间称为`线性空间`。也可以理解为`x1` 和 `x2` 之间是线性关系，还是非线性关系。


## 多层感知机

虽然单层感知机无法表示 `XOR` 门，但是正如可以使用 `NAND` 来不断叠加，表示出任何逻辑电路一样。我们也可以使用多个感知机的叠加，来表示`XOR`逻辑。
```hdl
/**
 * Exclusive-or gate:
 * out = not (a == b)
 */

CHIP Xor {
    IN a, b;
    OUT out;

    PARTS:
    // Put your code here:
    // xor = (a || b) && !(a && b)
    //     = (a || b) && (!a || !b)
    //     = (a&&!b) || (!a&&b)
    Not (in=b, out=Notb);
    And (a=a, b=Notb, out=aAndNotb);
    Not (in=a, out=Nota);
    And (a=Nota, b=b, out=NotaAndb);
    Or (a=aAndNotb, b=NotaAndb, out=out);
}
```
上面是我在<nand2tetris> 中的HDL电路设计作业。

```cpp
bool xor_neural(int a, int b) {
    bool s1 = nand_neural(a, b);
    bool s2 = or_neural(a, b);
    return and_neural(s1, s2);
}
```

多层感知机之所以可以在空间内，表示出曲线来分割出 非线形空间，原因是经过多层线性函数后，输入之间的关系转变为非线形的。

## 多层感知机的层数定义

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1659878447811/mcoqH1OqH.png align="left")

图中的感知机总共由 3 层构成，但是因为拥有权重的层实质 上只有2层(第0层和第1层之间，第1层和第2层之间)，所以称 为“2 层感知机”。

## 感知机构建计算机

理论上可以说 2 层感知机就能构建计算机。这是因为，已有研究证明， 2层感知机(严格地说是激活函数使用了非线性的sigmoid函数的感知机，具体请参照下一章)可以表示任意函数。

PS: 本文之前提到的感知机，激活函数均是阶跃函数。

