线性与非线性神经网络?

nir*_*oot 14 neural-network tensorflow

我是机器学习和神经网络的新手.我知道如何构建非线性分​​类模型,但我目前的问题是连续输出.我一直在寻找有关神经网络回归的信息,但我遇到的只是关于线性回归的信息- 没有关于非线性情况的信息.这很奇怪,因为为什么有人会使用神经网络来解决简单的线性回归呢?这不就像用核弹杀死一只苍蝇吗?

所以我的问题是:什么使神经网络非线性?(隐藏层?非线性激活函数?)或者我对"线性"这个词有完全错误的理解 - 线性回归NN能否准确地模拟比y = aX + b更复杂的数据集?"线性"这个词是否与"逻辑"相反?

(我打算使用TensorFlow,但TensorFlow线性模型教程使用二进制分类问题作为示例,所以这对我也没有帮助.)

mar*_*ars 14

首先,神经网络可以模拟任何函数(不仅仅是线性函数)看看这个 - http://neuralnetworksanddeeplearning.com/chap4.html.

神经网络具有非线性激活层,这使得神经网络成为非线性元素.

用于关联输入和输出的功能由神经网络和它获得的训练量决定.如果你提供两个具有线性关系的变量,那么只要你没有过度拟合,你的网络就会学到这一点.同样,足够复杂的神经网络可以学习任何功能.


itw*_*kix 11

警告:我不提倡只使用线性激活函数,尤其是在简单的前馈架构中。

好的,我想我需要花一些时间明确地重写这个答案,因为很多人都误解了我试图提出的观点。

首先让我指出,我们可以谈论参数中的线性或变量中的线性

激活函数不一定是使神经网络非线性的原因(从技术上讲)。

例如,请注意以下回归预测值被视为线性预测,尽管输入是非线性变换,因为输出构成参数的线性组合(尽管该模型的变量是非线性的):

公式

现在为了简单起见,让我们考虑一个单神经元、单层神经网络:

公式

如果传递函数是线性的,则:

公式

您可能已经注意到,这是一个线性回归。即使我们要添加多个输入和神经元,每个输入和神经元都有一个线性激活函数,我们现在也只会有一个回归集合(它们的参数都是线性的,因此这个简单的神经网络是线性的):

公式

现在回到(3),让我们添加两层,这样我们就有了一个 3 层的神经网络,每层一个神经元(都具有线性激活函数):

公式 (第一层)

公式 (第二层)

现在注意:

公式

减少到:

公式

在哪里 公式公式

这意味着我们的两层网(各自具有单个神经元)是线性在其参数尽管在网络是线性的每激活函数; 然而,它在变量中仍然是线性的。 因此,一旦训练完成,模型的变量和参数都将是线性的。这两者都很重要,因为您无法使用单个回归复制这个简单的两层网络,并且仍然捕获模型的所有影响。 此外,让我明确说明:如果您使用具有多层的模型,则不能保证其变量中的输出是非线性的(如果您使用简单的 MLP 感知器和线激活函数,您的图片仍将是一个线)。

话虽如此,让我们来看看@Pawu关于这个答案的以下声明:

答案是非常具有误导性的,而且听起来,我们可以仅使用线性变换来学习非线性关系,这根本不是真的。当我们反向传播时,我们采用单个权重 w1 的导数并修复其他所有内容。现在如上所述,我们仍在研究线性函数。

虽然您可能会争辩说@Pawu 所说的在技术上是正确的,但我认为他们在暗示:

答案是非常具有误导性的,而且听起来,我们可以仅使用线性激活函数来学习非线性关系,这根本不是真的。

我认为这个修改后的陈述是错误的,很容易被证明是不正确的。对模型的架构有一个隐含的假设。确实,如果您限制自己使用某些网络架构,则您无法在没有激活函数的情况下引入非线性,但这是一个任意限制,并不能推广到所有网络模型。

让我把它具体化。首先考虑一个简单的异或问题。这是一个基本的分类问题,您试图在配置中建立数据点之间的边界,如下所示:

异或问题

这个问题的关键在于它不是线性可分的,这意味着没有一条直线能够完美地分类。现在,如果你在互联网上的任何地方阅读,我相信他们会说这个问题不能通过使用神经网络的线性激活函数来解决(注意没有关于架构的说明)。这种说法只在极其有限的上下文中是正确的,并且通常是错误的。

请允许我演示一下。下面是一个非常简单的手写神经网络。该网络采用 -1 和 1 之间随机生成的权重,一个定义架构的“xor_network”函数(注意没有 sigmoid、hardlims 等,只有 mX 或 MX + B 形式的线性变换),并使用标准反向传播进行训练:

#%% Packages
import numpy as np

#%% Data
data = np.array([[0, 0, 0],[0, 1, 1],[1, 0, 1],[1, 1, 0]])
np.random.shuffle(data)
train_data = data[:,:2]
target_data = data[:,2]

#%% XOR architecture
class XOR_class():

    def __init__(self, train_data, target_data, alpha=.1, epochs=10000):
        self.train_data = train_data
        self.target_data = target_data
        self.alpha = alpha
        self.epochs = epochs
        
        #Random weights
        self.W0 = np.random.uniform(low=-1, high=1, size=(2)).T
        self.b0 = np.random.uniform(low=-1, high=1, size=(1))
        self.W2 = np.random.uniform(low=-1, high=1, size=(2)).T
        self.b2 = np.random.uniform(low=-1, high=1, size=(1))

    #xor network (linear transfer functions only)
    def xor_network(self, X0):
        n0 = np.dot(X0, self.W0) + self.b0
        X1 = n0*X0
        a = np.dot(X1, self.W2) + self.b2
        return(a, X1)

    #Training the xor network
    def train(self):
        for epoch in range(self.epochs):
            for i in range(len(self.train_data)):
                # Forward Propagation:
                X0 = self.train_data[i]
                a, X1 = self.xor_network(X0)
        
                # Backward Propagation:
                e = self.target_data[i] - a
                s_2 = -2*e
        
                # Update Weights:
                self.W0 = self.W0 - (self.alpha*s_2*X0)
                self.b0 = self.b0 - (self.alpha*s_2)
                self.W2 = self.W2 - (self.alpha*s_2*X1)
                self.b2 = self.b2 - (self.alpha*s_2)
        
        #Restart training if we get lost in the parameter space.
        if np.isnan(a) or (a > 1) or (a < -1):
            print('Bad initialization, reinitializing.')
            self.W0 = np.random.uniform(low=-1, high=1, size=(2)).T
            self.b0 = np.random.uniform(low=-1, high=1, size=(1))
            self.W2 = np.random.uniform(low=-1, high=1, size=(2)).T
            self.b2 = np.random.uniform(low=-1, high=1, size=(1))
            self.train()
    
    #Predicting using the trained weights.
    def predict(self, test_data):
        for i in train_data:
            a, X1 = self.xor_network(i)
            #I cut off decimals past 12 for convienience, not necessary.
            print(f'input: {i} - output: {np.round(a, 12)}')
Run Code Online (Sandbox Code Playgroud)

现在让我们看一下输出:

#%% Execution
xor = XOR_class(train_data, target_data)
xor.train()
np.random.shuffle(data)
test_data = data[:,:2]
xor.predict(test_data)
Run Code Online (Sandbox Code Playgroud)
input: [1 0] - output: [1.]
input: [0 0] - output: [0.]
input: [0 1] - output: [1.]
input: [1 1] - output: [0.]
Run Code Online (Sandbox Code Playgroud)

你知道吗,我想我们可以只使用线性激活函数和多层来学习非线性关系(这是使用纯线激活函数的正确分类,不需要 sigmoid)。. .

这里唯一的问题是我截掉了 12 之后的所有小数,但老实说,7.3 X 10^-16 基本上是 0。

现在公平地说,我正在做一个小技巧,在那里我使用网络连接来获得非线性结果,但这就是我试图开车回家的重点: 神经网络非线性的神奇之处在于层,而不仅仅是激活功能

因此,您的问题“是什么使神经网络非线性”的答案是:参数中的非线性,或者显然是变量中的非线性

参数/变量中的这种非线性来自两种方式:1) 在您的网络中具有不止一层的神经元(如上所示),或 2) 具有导致权重非线性的激活函数。

举一个关于通过激活函数产生非线性的例子,假设我们的输入空间、权重和偏差都受到约束,使得它们都严格为正(为简单起见)。现在使用(2)(单层,单神经元)和激活函数公式,我们有以下内容:

公式

减少到:

公式

在哪里 公式, 公式, 和 公式

现在,忽略这个神经网络有什么问题,应该很清楚,至少,它在参数和变量中是非线性的,并且非线性是完全通过选择激活函数引入的。

最后,是的,神经网络可以对使用线性模型无法建模的复杂数据结构进行建模(参见上面的 xor 示例)。

编辑:

正如@hH1sG0n3 所指出的,参数中的非线性并不直接来自许多常见的激活函数(例如 sigmoid)。这并不是说常见的激活函数不会使神经网络非线性(因为它们在变量中是非线性的),而是它们引入的非线性是退化的,没有参数非线性。例如,具有 sigmoid 激活函数的单层 MLP 将产生变量中非线性的输出,因为输出与输入不成比例,但实际上这只是一组广义线性模型。如果我们要通过适当的链接函数转换目标,这应该特别明显,现在激活函数将是线性的。现在这并不是说激活函数不 t 在神经网络的非线性中发挥重要作用(显然它们确实如此),但它们的作用更多是改变/扩展解空间。换句话说,参数中的非线性(通常通过许多层/连接表示)对于超越回归的非退化解决方案是必要的。当我们有一个参数非线性的模型时,我们将拥有与回归完全不同的野兽。

归根结底,我想通过这篇文章指出神经网络的“魔力”也在层中,并消除普遍存在的神话,即具有线性激活函数的多层神经网络总是一堆线性回归。

  • Xor 网络必须包含非线性变换来建模非线性关系,但我在这里认为这种非线性的载体是层连接,而不是非线性激活函数的任意选择。 (2认同)