感知器无法学习的原因是什么?

人工智能 训练 感知器
2021-11-11 01:16:08

我刚刚开始学习神经网络,我决定研究一个简单的 3 输入感知器来开始学习。我也只使用二进制输入来全面了解感知器的工作原理。我很难理解为什么某些培训输出有效而其他培训输出无效。我猜这与输入数据的线性可分性有关,但我不清楚如何轻松确定这一点。我知道绘图线测试,但我不清楚如何绘制输入数据以完全了解什么可行,什么不可行。

后面有很多信息。但这一切都非常简单。我将所有这些信息包括在内,以便清楚地了解我在做什么以及试图理解和学习。

这是我正在建模的简单 3 输入感知器的示意图。

3 输入感知器

因为它只有 3 个输入并且它们是二进制(0 或 1),所以只有 8 种可能的输入组合。但是,这也允许 8 个可能的输出。这允许训练 256 个可能的输出。换句话说,感知器可以被训练来识别一种以上的输入配置。

让我们称输入 0 到 7(3 输入二进制系统的所有可能配置)。但是我们可以训练感知器识别不止一个输入。换句话说,我们可以训练感知器触发从 0 到 3 的任何输入,而不是针对输入 4 到 7。所有这些可能的组合加起来有 256 种可能的训练输入状态。

其中一些训练输入状态有效,而另一些则无效。我正在尝试学习如何确定哪些训练集有效,哪些无效。

我用 Python 编写了以下程序,通过所有 256 种可能的训练状态来模拟这个感知器。

这是此仿真的代码:

import numpy as np
np.set_printoptions(formatter={'float': '{: 0.1f}'.format})

# Perceptron math fucntions. 
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
def sigmoid_derivative(x):
    return x * (1 - x)
# END Perceptron math functions.

# The first column of 1's is used as the bias.  
# The other 3 cols are the actual inputs, x3, x2, and x1 respectively
training_inputs = np.array([[1, 0, 0, 0],
                         [1, 0, 0, 1],
                         [1, 0, 1, 0],
                         [1, 0, 1, 1],
                         [1, 1, 0, 0],
                         [1, 1, 0, 1],
                         [1, 1, 1, 0],
                         [1, 1, 1, 1]])

# Setting up the training outputs data set array                         
num_array = np.array
num_array = np.arange(8).reshape([1,8])
num_array.fill(0)

for num in range(25):
    bnum = bin(num).replace('0b',"").rjust(8,"0")
    for i in range(8):
        num_array[0,i] = int(bnum[i])

    training_outputs = num_array.T
# training_outputs will have the array form: [[n,n,n,n,n,n,n,n]]
# END of setting up training outputs data set array                      

    # -------  BEGIN Perceptron functions ----------
    np.random.seed(1)
    synaptic_weights = 2 * np.random.random((4,1)) - 1
    for iteration in range(20000):
        input_layer = training_inputs
        outputs = sigmoid(np.dot(input_layer, synaptic_weights))
        error = training_outputs - outputs
        adjustments = error * sigmoid_derivative(outputs)
        synaptic_weights += np.dot(input_layer.T, adjustments)
    # -------  END Perceptron functions ----------


    # Convert to clean output 0, 0.5, or 1 instead of the messy calcuated values.
    # This is to make the printout easier to read.
    # This also helps with testing analysis below.
    for i in range(8):
        if outputs[i] <= 0.25:
            outputs[i] = 0
        if (outputs[i] > 0.25 and outputs[i] < 0.75):
            outputs[i] = 0.5
        if outputs[i] > 0.75:
            outputs[i] = 1
    # End convert to clean output values.

    # Begin Testing Analysis
    # This is to check to see if we got the correct outputs after training.
    evaluate = "Good"
    test_array = training_outputs
    for i in range(8):
        # Evaluate for a 0.5 error.
        if outputs[i] == 0.5:
            evaluate = "The 0.5 Error"
            break
        # Evaluate for incorrect output
        if outputs[i] != test_array[i]:
            evaluate = "Wrong Answer"
    # End Testing Analysis

    # Printout routine starts here:
    print_array = test_array.T
    print("Test#: {0}, Training Data is: {1}".format(num, print_array[0]))
    print("{0}, {1}".format(outputs.T, evaluate))
    print("") 

当我运行这段代码时,我得到了前 25 个训练测试的以下输出。

Test#: 0, Training Data is: [0 0 0 0 0 0 0 0]
[[ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0]], Good

Test#: 1, Training Data is: [0 0 0 0 0 0 0 1]
[[ 0.0 0.0 0.0 0.0 0.0 0.0 0.0 1.0]], Good

Test#: 2, Training Data is: [0 0 0 0 0 0 1 0]
[[ 0.0 0.0 0.0 0.0 0.0 0.0 1.0 0.0]], Good

Test#: 3, Training Data is: [0 0 0 0 0 0 1 1]
[[ 0.0 0.0 0.0 0.0 0.0 0.0 1.0 1.0]], Good

Test#: 4, Training Data is: [0 0 0 0 0 1 0 0]
[[ 0.0 0.0 0.0 0.0 0.0 1.0 0.0 0.0]], Good

Test#: 5, Training Data is: [0 0 0 0 0 1 0 1]
[[ 0.0 0.0 0.0 0.0 0.0 1.0 0.0 1.0]], Good

Test#: 6, Training Data is: [0 0 0 0 0 1 1 0]
[[ 0.0 0.0 0.0 0.0 0.5 0.5 0.5 0.5]], The 0.5 Error

Test#: 7, Training Data is: [0 0 0 0 0 1 1 1]
[[ 0.0 0.0 0.0 0.0 0.0 1.0 1.0 1.0]], Good

Test#: 8, Training Data is: [0 0 0 0 1 0 0 0]
[[ 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0]], Good

Test#: 9, Training Data is: [0 0 0 0 1 0 0 1]
[[ 0.0 0.0 0.0 0.0 0.5 0.5 0.5 0.5]], The 0.5 Error

Test#: 10, Training Data is: [0 0 0 0 1 0 1 0]
[[ 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0]], Good

Test#: 11, Training Data is: [0 0 0 0 1 0 1 1]
[[ 0.0 0.0 0.0 0.0 1.0 0.0 1.0 1.0]], Good

Test#: 12, Training Data is: [0 0 0 0 1 1 0 0]
[[ 0.0 0.0 0.0 0.0 1.0 1.0 0.0 0.0]], Good

Test#: 13, Training Data is: [0 0 0 0 1 1 0 1]
[[ 0.0 0.0 0.0 0.0 1.0 1.0 0.0 1.0]], Good

Test#: 14, Training Data is: [0 0 0 0 1 1 1 0]
[[ 0.0 0.0 0.0 0.0 1.0 1.0 1.0 0.0]], Good

Test#: 15, Training Data is: [0 0 0 0 1 1 1 1]
[[ 0.0 0.0 0.0 0.0 1.0 1.0 1.0 1.0]], Good

Test#: 16, Training Data is: [0 0 0 1 0 0 0 0]
[[ 0.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0]], Good

Test#: 17, Training Data is: [0 0 0 1 0 0 0 1]
[[ 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0]], Good

Test#: 18, Training Data is: [0 0 0 1 0 0 1 0]
[[ 0.0 0.0 0.5 0.5 0.0 0.0 0.5 0.5]], The 0.5 Error

Test#: 19, Training Data is: [0 0 0 1 0 0 1 1]
[[ 0.0 0.0 0.0 1.0 0.0 0.0 1.0 1.0]], Good

Test#: 20, Training Data is: [0 0 0 1 0 1 0 0]
[[ 0.0 0.5 0.0 0.5 0.0 0.5 0.0 0.5]], The 0.5 Error

Test#: 21, Training Data is: [0 0 0 1 0 1 0 1]
[[ 0.0 0.0 0.0 1.0 0.0 1.0 0.0 1.0]], Good

Test#: 22, Training Data is: [0 0 0 1 0 1 1 0]
[[ 0.0 0.0 0.0 1.0 0.0 1.0 1.0 1.0]], Wrong Answer

Test#: 23, Training Data is: [0 0 0 1 0 1 1 1]
[[ 0.0 0.0 0.0 1.0 0.0 1.0 1.0 1.0]], Good

Test#: 24, Training Data is: [0 0 0 1 1 0 0 0]
[[ 0.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0]], Wrong Answer

在大多数情况下,它似乎正在工作。但是在某些情况下它显然不起作用。

我有两种不同的标签。

第一种错误是很容易看到的“0.5 错误”。在这种情况下,它永远不应该返回 0.5 的任何输出。一切都应该是二进制的。第二种错误是当它报告正确的二进制输出但它们与训练识别的内容不匹配时。

我想了解这些错误的原因。我对尝试纠正错误不感兴趣,因为我相信这些是有效的错误。换句话说,这些是感知器根本无法训练的情况。没关系。

我想了解的是为什么这些案例是无效的。我怀疑它们与在这些情况下无法线性分离的输入数据有关。但如果是这样的话,那么我该如何确定哪些情况不是线性可分的呢?如果我能理解如何做到这一点,我会非常高兴。

另外,它在特定情况下不起作用的原因是否相同?换句话说,这两种类型的错误都是由输入数据的线性不可分性引起的吗?或者是否有不止一种情况会导致感知器在某些训练情况下失败。

任何帮助,将不胜感激。

1个回答

更新:

我自己找到了答案。

首先,我想出了如何使用 matplotlib 在散点图上绘制输入数据和输出训练数据。一旦我能够做到这一点,我就可以立即确切地看到正在发生的事情。

当答案正确时,输入数据确实可以根据训练输出要识别的内容进行线性可分。

当发生单次违反线性可分性时,就会出现“0.5 错误”。

当两次违反线性分离时,会出现“错误答案”错误。换言之,存在在两个单独的平面中违反线性分离的情况。或者当数据可以通过平面分隔但需要多个平面来执行此操作时。(见下图示例)

我怀疑这些不同类型的错误之间会有区别,答案是,是的,有区别。

所以我已经解决了我自己的问题。感谢任何可能一直致力于此的人。

如果你想看看我的一些图表,这里有一些具体的例子:

所有可能的二进制输入的图表

所有可能的输入

一个好的training_outputs = np.array([0, 0, 0, 0, 1, 1, 1, 0])的例子。正如您在此图中看到的那样,红点与蓝点是线性可分的。

良好的培训示例

一个 0.5 错误的例子,training_outputs = np.array([0, 0, 0, 1, 0, 0, 1, 0])。这些点在一个平面上不是线性可分的

0.5 错误示例

2 平面错误答案错误示例,training_outputs = np.array([0, 0, 0, 1, 0, 1, 1, 0])。可以看到有两个平面不是线性可分的

错误答案 2 飞机违例

另一种错误答案的示例,training_outputs = np.array([0, 0, 0, 1, 1, 0, 0, 0])。在这种情况下,数据可以通过平面分隔,但需要 2 个不同的平面来执行此操作,而感知器无法处理。

需要两架飞机分开的错误答案

所以这涵盖了所有可能的错误情况。图表不是很好!