使用’tanh’作为激活函数和’交叉熵’作为成本函数的神经网络不起作用

我实现了一个简单的neural network。它在使用’sigmoid + 交叉熵’、’sigmoid + 二次成本’和’tanh + 二次成本’时运行良好,但使用’tanh + 交叉熵’时不起作用(效果不比随机猜测好)。请问有人能帮我找出原因吗?请看FullConnectedLayer的代码:

class FullConnectedLayer(BaseLayer):    """    FullConnectedLayer
    ~~~~~~~~~~~~~~~~~~~~
    数据成员:
     sizes       ---- <type list> 网络的尺寸
    n_layers    ---- <type int> 子层数量
    activation  ---- <type Activation> 神经元的激活函数
    weights     ---- <type list> 存储权重
    biases      ---- <type list> 存储偏置
    neurons     ---- <type list> 存储神经元的状态(输出)
    zs          ---- <type list> 存储加权输入到神经元
    grad_w      ---- <type list> 存储成本相对于权重的梯度
    grad_b      ---- <type list> 存储成本相对于偏置的梯度
    ---------------------
    方法:
    __init__(self, sizes, activation = Sigmoid())
    size(self)
    model(self)
    feedforward(self, a)
    backprop(self, C_p)
    update(self, eta, lmbda, batch_size, n)
    """
    def __init__(self, sizes, activation = Sigmoid(), normal_initialization = False):
        """
        列表''sizes''包含网络各层的神经元数量。例如,sizes = [2, 3, 2]表示3层,第1层有2个神经元,第2层有3个神经元,第3层有2个神经元。
        请注意,输入层可能由其他类型的层传递,当连接在该层之后,我们不为此层设置偏置。
        还请注意,如果输出层连接在该层之前,则传递给其他层,在这种情况下,只需将输出分配给其输入即可。
        例如,Layer1([3, 2, 4])->Layer2([4, 6, 3])->Layer3([3, 2])。只需将Layer1的输出分配给Layer2的输入,这样就安全了。
        """
        BaseLayer.__init__(self, sizes, activation)
        if normal_initialization:
            self.weights = [np.random.randn(j, i)
                    for i, j in zip(sizes[:-1], sizes[1:])]
        else:
            self.weights = [np.random.randn(j, i) / np.sqrt(i)
                    for i, j in zip(sizes[:-1], sizes[1:])]
        self.biases = [np.random.randn(j, 1) for j in sizes[1:]]
        self.grad_w = [np.zeros(w.shape) for w in self.weights]
        self.grad_b = [np.zeros(b.shape) for b in self.biases]
    def feedforward(self, a):
        """
        如果''a''是输入,返回网络的输出。
        """
        self.neurons = [a] # 存储所有层的激活值(输出)
        self.zs = []
        for w, b in zip(self.weights, self.biases):
            z = np.dot(w, self.neurons[-1]) + b
            self.zs.append(z)
            self.neurons.append(self.activation.func(z))
        return self.neurons[-1]
    def backprop(self, Cp_a):
        """
        反向传播误差。
        ------------------------------
        返回一个元组,其第一部分是权重和偏置的梯度列表,第二部分是反向传播的误差。
        Cp_a, dC/da: 成本函数相对于a的导数,a是神经元的输出。
        """
        # 最后一层
        delta = Cp_a * self.activation.prime(self.zs[-1])
        self.grad_b[-1] += delta
        self.grad_w[-1] += np.dot(delta, self.neurons[-2].transpose())
        for l in range(2, self.n_layers):
            sp = self.activation.prime(self.zs[-l])  # a.prime(z)
            delta = np.dot(self.weights[-l + 1].transpose(), delta) * sp
            self.grad_b[-l] += delta
            self.grad_w[-l] += np.dot(delta, self.neurons[-l - 1].transpose())
        Cp_a_out = np.dot(self.weights[0].transpose(), delta)
        return Cp_a_out
    def update(self, eta, lmbda, batch_size, n):
        """
        通过应用梯度下降算法更新网络的权重和偏置。
        ''eta''是学习率
        ''lmbda''是正则化参数
        ''n''是训练数据集的总大小
        """
        self.weights = [(1 - eta * (lmbda/n)) * w - (eta/batch_size) * delta_w\
                for w, delta_w in zip(self.weights, self.grad_w)]
        self.biases = [ b - (eta / batch_size) * delta_b\
                for b, delta_b in zip(self.biases, self.grad_b)]
        # 清除''grad_w''和''grad_b'',以便它们不会被添加到下一次更新中
        for dw, db in zip(self.grad_w, self.grad_b):
            dw.fill(0)
            db.fill(0)

这是tanh函数的代码:

class Tanh(Activation):    @staticmethod    def func(z):        """ 功能。 """        return (np.exp(z) - np.exp(-z)) / (np.exp(z) + np.exp(-z))    @staticmethod    def prime(z):        """ 导数。 """        return 1. - Tanh.func(z) ** 2

这是交叉熵类的代码:

class CrossEntropyCost(Cost):    @staticmethod    def func(a, y):        """        返回与输出''a''和期望输出''y''相关的成本。
        请注意,使用np.nan_to_num来确保数值稳定性。特别是,如果''a''和''y''在同一个位置都有1.0,
        那么表达式(1-y) * np.log(1-a)会返回nan。np.nan_to_num确保将其转换为正确的值(0.0)。
        """
        for ai in a:            if ai < 0:                print("在CrossEntropyCost.func(a, y)中...需要a_i > 0,a_i属于a。")                exit(1)        return np.sum(np.nan_to_num(-y * np.log(a) - (1-y) * np.log(1-a)))    @staticmethod    def Cp_a(a, y):        """        Cp_a, dC/da: C相对于a的导数
        ''a''是神经元的输出
        ''y''是神经元的期望输出
        """
        #return (a - y) # delta
        return (a - y) / (a * (1 - a))

编辑:看起来问题是tanh的范围是-1到+1,这对于交叉熵来说是非法的。但是,如果我只想使用tanh激活和交叉熵成本,应该如何处理呢?


回答:

看起来你在输出层使用了tanh,而tanh的范围是-1, +1,期望的输出在0, +1范围内。这对于产生0, +1范围输出的Sigmoid来说无关紧要。

Related Posts

使用LSTM在Python中预测未来值

这段代码可以预测指定股票的当前日期之前的值,但不能预测…

如何在gensim的word2vec模型中查找双词组的相似性

我有一个word2vec模型,假设我使用的是googl…

dask_xgboost.predict 可以工作但无法显示 – 数据必须是一维的

我试图使用 XGBoost 创建模型。 看起来我成功地…

ML Tuning – Cross Validation in Spark

我在https://spark.apache.org/…

如何在React JS中使用fetch从REST API获取预测

我正在开发一个应用程序,其中Flask REST AP…

如何分析ML.NET中多类分类预测得分数组?

我在ML.NET中创建了一个多类分类项目。该项目可以对…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注