神经网络极简入门

神经网络是深度学习的基础,正是深度学习的兴起,让停滞不前的人工智能再一次的取得飞速的发展。

其实神经网络的理论由来已久,灵感来自仿生智能计算,只是以前限于硬件的计算能力,没有突出的表现,
直至谷歌的AlphaGO的出现,才让大家再次看到神经网络相较于传统机器学习的优异表现。

本文主要介绍神经网络中的重要基础概念,然后基于这些概念手工实现一个简单的神经网络。
希望通过理论结合实践的方式让大家更容易的理解神经网络。

1. 神经网络是什么

神经网络就像人脑一样,整体看上去非常复杂,但是其基础组成部分并不复杂。
其组成部分中最重要的就是 神经元 neural ), sigmod函数 layer )。

1.1. 神经元

神经元( neural )是神经网络最基本的元素,一个神经元包含3个部分:

  • 获取输入 :获取多个输入的数据
  • 数学处理 :对输入的数据进行数学计算
  • 产生输出 :计算后 多个 输入数据变成 一个 输出数据


从上图中可以看出, 神经元 中的处理有2个步骤。
第一个步骤: 从蓝色框变成红色框,是对输入的数据进行加权计算后合并为一个值( N )。
\(N = x_1w_1 + x_2w_2\) 其中, \(w_1,w_2\) 分别是输入数据 \(x_1,x_2\) 的权重。
一般在计算 \(N\) 的过程中,除了权重,还会加上一个偏移参数 \(b\) ,最终得到:
\(N = x_1w_1 + x_2w_2+b\)

第二个步骤 :从红色框变成绿色框,通过 sigmoid函数 是对 N 进一步加工得到的神经元的最终输出( M )。

1.2. sigmoid函数

sigmoid 函数也被称为 S函数 ,因为的形状类似 S形

它是神经元中的重要函数,能够将输入数据的值映射到 \((0,1)\) 之间。
最常用的 sigmoid 函数是 \(f(x)=\frac{1}{1+e^{-x}}\) ,当然,不是只有这一种 sigmoid 函数。

至此,神经元通过两个步骤,就把输入的多个数据,转换为一个 \((0,1)\) 之间的值。

1.3. 层

多个神经元可以组合成一层,一个神经网络一般包含一个输入层和一个输出层,以及多个隐藏层。

比如上图中,有2个隐藏层,每个隐藏层中分别有4个和2个神经元。
实际的神经网络中, 隐藏层数量 和其中的 神经元数量 都是不固定的,根据模型实际的效果来进行调整。

1.4. 网络

通过神经元和层的组合就构成了一个网络,神经网络的名称由此而来。
神经网络可大可小,可简单可复杂,不过,太过简单的神经网络模型效果一般不会太好。

因为一只果蝇就有10万个神经元,而人类的大脑则有大约1000亿个神经元,
这就是为什么训练一个可用的神经网络模型需要庞大的算力,这也是为什么神经网络的理论 1943年 就提出了,
但是基于深度学习的 AlphaGO 却诞生于 2015年

2. 实现一个神经网络

了解上面的基本概念只能形成一个感性的认知。
下面通过自己动手实现一个最简单的神经网络,来进一步认识 神经元 sigmoid函数 以及 隐藏层 是如何发挥作用的。

2.1. 准备数据

数据使用 sklearn 库中经典的鸢尾花数据集,这个数据集中有3个分类的鸢尾花,每个分类50条数据。
为了简化,只取其中前 100 条数据来使用,也就是取 2个分类 的鸢尾花数据。

from sklearn.datasets import load_iris

ds = load_iris(as_frame=True, return_X_y=True)
data = ds[0].iloc[:100]
target = ds[1][:100]

print(data)
print(target)


变量 data 100 条数据,每条数据包含 4个属性 ,分别是花萼的宽度和长度,花瓣的宽度和长度。


变量 target 中也是 100 条数据,只有 0和1 两种值,表示两种不同种类的鸢尾花。

2.2. 实现神经元

准备好了数据,下面开始逐步实现一个简单的神经网络。
首先,实现最基本的单元-- 神经元
本文第一节中已经介绍了神经元中主要的2个步骤,分别计算出 \(N\) \(M\)

计算 \(N\) 时,依据每个输入元素的权重( \(w_1,w_2\) )和整体的偏移 \(b\)
计算 \(M\) 时,通过 sigmoid 函数。

def sigmoid(x):
    return 1 / (1 + np.exp(-1 * x))

@dataclass
class Neuron:
    weights: list[float] = field(default_factory=lambda: [])
    bias: float = 0.0
    N: float = 0.0
    M: float = 0.0

    def compute(self, inputs):
        self.N = np.dot(self.weights, inputs) + self.bias
        self.M = sigmoid(self.N)
        return self.M

上面的代码中, Neuron 类表示神经元,这个类有4个属性:
其中属性 weights bias 是计算 \(N\) 时的权重和偏移;
属性 N M 分别是神经元中两步计算的结果。

Neuron 类的 compute 方法根据输入的数据计算神经元的输出。

2.3. 实现神经网络

神经元实现之后,下面就是构建神经网络。
我们的输入数据是带有 4个属性 (花萼的宽度和长度,花瓣的宽度和长度)的鸢尾花数据,
所以神经网络的输入层有4个值( \(x_1,x_2,x_3,x_4\) )。

为了简单起见,我们的神经网络只构建一个 隐藏层 ,其中包含 3个神经元
最后就是输出层,输出层最后输出一个值,表示鸢尾花的种类。

由此形成的简单神经网络如下图所示:

实现的代码:

@dataclass
class MyNeuronNetwork:
    HL1: Neuron = field(init=False)
    HL2: Neuron = field(init=False)
    HL3: Neuron = field(init=False)
    O1: Neuron = field(init=False)

    def __post_init__(self):
        self.HL1 = Neuron()
        self.HL1.weights = np.random.dirichlet(np.ones(4))
        self.HL1.bias = np.random.normal()

        self.HL2 = Neuron()
        self.HL2.weights = np.random.dirichlet(np.ones(4))
        self.HL2.bias = np.random.normal()

        self.HL3 = Neuron()
        self.HL3.weights = np.random.dirichlet(np.ones(4))
        self.HL3.bias = np.random.normal()

        self.O1 = Neuron()
        self.O1.weights = np.random.dirichlet(np.ones(3))
        self.O1.bias = np.random.normal()

    def compute(self, inputs):
        m1 = self.HL1.compute(inputs)
        m2 = self.HL2.compute(inputs)
        m3 = self.HL3.compute(inputs)

        output = self.O1.compute([m1, m2, m3])
        return output

MyNeuronNetwork 类是自定义的神经网络,其中的属性是 4个神经元
HL1 HL2 HL3 隐藏层 的3个神经元; O1 输出层 的神经元。

__post__init__ 函数是为了初始化各个神经元。
因为输入层是4个属性( \(x_1,x_2,x_3,x_4\) ),所以神经元 HL1 HL2 HL3 weights 初始化为 4个 随机数组成的列表,
偏移( bias )用一个随时数来初始化。

对于神经元 O1 ,它的输入是隐藏层的3个神经元,所以它的 weights 初始化为 3个 随机数组成的列表,
偏移( bias )还是用一个随时数来初始化。

最后还有一个 compute 函数,这个函数描述的就是整个神经网络的计算过程。
首先,根据输入层( \(x_1,x_2,x_3,x_4\) )的数据计算隐藏层的神经元( HL1 HL2 HL3 );
然后,以隐藏层的神经元( HL1 HL2 HL3 )的输出作为输出层的神经元( O1 )的输入,并将 O1 的计算结果作为整个神经网络的输出。

2.4. 训练模型

上面的神经网络中各个神经元的中的参数(主要是 weights bias )都是随机生成的,
所以直接使用这个神经网络,效果一定不会很好。
所以,我们需要给神经网络( MyNeuronNetwork 类)加一个训练函数,用来训练神经网络中各个神经元的参数(也就是个各个神经元中的 weights bias )。

@dataclass
class MyNeuronNetwork:
    # 略...

    def train(self, data: pd.DataFrame, target: pd.Series):
        ## 使用 随机梯度下降算法来训练
        pass

上面的 train 函数有两个参数 data (训练数据)和 target (训练数据的标签)。
我们使用 随机梯度下降算法 来训练模型的参数。
这里略去了具体的代码,完整的代码可以在文章的末尾下载。

此外,再实现一个预测函数 predict ,传入测试数据集,
然后用我们训练好的神经网络模型来预测测试数据集的标签。

@dataclass
class MyNeuronNetwork:
    # 略...
    
    def predict(self, data: pd.DataFrame):

        results = []
        for idx, row in enumerate(data.values):
            pred = self.compute(row)
            results.append(round(pred))

        return results

2.5. 验证模型效果

最后就是验证模型的效果。

def main():
    # 加载数据
    ds = load_iris(as_frame=True, return_X_y=True)

    # 只用前100条数据
    data = ds[0].iloc[:100]
    target = ds[1][:100]

    # 划分训练数据,测试数据
    # test_Size=0.2 表示80%作为训练数据,20%作为测试数据
    X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2)

    # 创建神经网络
    nn = MyNeuronNetwork()

    # 用训练数据集来训练模型
    nn.train(X_train, y_train)

    # 检验模型
    # 用训练过的模型来预测测试数据的标签
    results = nn.predict(X_test)
    df = pd.DataFrame()
    df["预测值"] = results
    df["实际值"] = y_test.values
    print(df)

运行结果可以看出,模型的效果还不错,20条测试数据的预测结果都正确。

3. 总结

本文中的的神经网络示例是为了介绍神经网络的一些基本概念,所以对神经网络做了尽可能的简化,为了方便去手工实现。

而实际环境中的神经网络,不仅神经元的个数,隐藏层的数量极其庞大,而且其计算和训练的方式也很复杂,手工去实现不太可能,
一般会借助 TensorFlow Keras PyTorch 等等知名的python深度学习库来帮助我们实现。

4. 代码下载

代码量不大,总共也就200行不到,感兴趣的话可以下载后运行试试。
simple_nn.zip: https://url11.ctfile.com/f/45455611-1242350800-e67991?p=6872 (访问密码: 6872)

热门手游下载
下载排行榜