反向传播神经网络

Posted

技术标签:

【中文标题】反向传播神经网络【英文标题】:Backpropagation neural network 【发布时间】:2015-03-03 01:40:35 【问题描述】:

我需要在我的应用程序中使用进行多类分类。我找到了this code 并尝试使其适应我的需要。它基于 Andrew Ng 在 Coursera 中的机器学习演讲。 我已经在 IRIS 数据集中对其进行了测试并取得了很好的结果(分类准确度在 0.96 左右),而在我的真实数据上却得到了糟糕的结果。我假设存在一些实现错误,因为数据非常简单。但我无法弄清楚到底是什么问题。

哪些参数是有意义的调整? 我试过了:

隐藏层中的单元数 泛化参数 (lambda) 最小化函数的迭代次数

这段代码中使用的内置最小化函数让我很困惑。正如@goncalopp 在评论中提到的那样,它只使用一次。它不应该迭代更新权重吗?如何实现?

这是我的训练数据(目标类在最后一列):


65535, 3670, 65535, 3885, -0.73, 1
65535, 3962, 65535, 3556, -0.72, 1
65535, 3573, 65535, 3529, -0.61, 1
3758, 3123, 4117, 3173, -0.21, 0
3906, 3119, 4288, 3135, -0.28, 0
3750, 3073, 4080, 3212, -0.26, 0
65535, 3458, 65535, 3330, -0.85, 2
65535, 3315, 65535, 3306, -0.87, 2
65535, 3950, 65535, 3613, -0.84, 2
65535, 32576, 65535, 19613, -0.35, 3
65535, 16657, 65535, 16618, -0.37, 3
65535, 16657, 65535, 16618, -0.32, 3

依赖关系这么明显,我觉得应该这么容易分类吧……

但结果很糟糕。我得到 0.6 到 0.8 的准确度。这绝对不适合我的应用程序。有人可以指出我可以做出哪些改进以取得更好的结果。

代码如下:

import numpy as np
from scipy import optimize

from sklearn import cross_validation
from sklearn.metrics import accuracy_score
import math

class NN_1HL(object):

    def __init__(self, reg_lambda=0, epsilon_init=0.12, hidden_layer_size=25, opti_method='TNC', maxiter=500):
        self.reg_lambda = reg_lambda
        self.epsilon_init = epsilon_init
        self.hidden_layer_size = hidden_layer_size
        self.activation_func = self.sigmoid
        self.activation_func_prime = self.sigmoid_prime
        self.method = opti_method
        self.maxiter = maxiter

    def sigmoid(self, z):
        return 1 / (1 + np.exp(-z))

    def sigmoid_prime(self, z):
        sig = self.sigmoid(z)
        return sig * (1 - sig)

    def sumsqr(self, a):
        return np.sum(a ** 2)

    def rand_init(self, l_in, l_out):
        self.epsilon_init = (math.sqrt(6))/(math.sqrt(l_in + l_out))
        return np.random.rand(l_out, l_in + 1) * 2 * self.epsilon_init - self.epsilon_init

    def pack_thetas(self, t1, t2):
        return np.concatenate((t1.reshape(-1), t2.reshape(-1)))

    def unpack_thetas(self, thetas, input_layer_size, hidden_layer_size, num_labels):
        t1_start = 0
        t1_end = hidden_layer_size * (input_layer_size + 1)
        t1 = thetas[t1_start:t1_end].reshape((hidden_layer_size, input_layer_size + 1))
        t2 = thetas[t1_end:].reshape((num_labels, hidden_layer_size + 1))
        return t1, t2

    def _forward(self, X, t1, t2):
        m = X.shape[0]
        ones = None
        if len(X.shape) == 1:
            ones = np.array(1).reshape(1,)
        else:
            ones = np.ones(m).reshape(m,1)

        # Input layer
        a1 = np.hstack((ones, X))

        # Hidden Layer
        z2 = np.dot(t1, a1.T)
        a2 = self.activation_func(z2)
        a2 = np.hstack((ones, a2.T))

        # Output layer
        z3 = np.dot(t2, a2.T)
        a3 = self.activation_func(z3)
        return a1, z2, a2, z3, a3

    def function(self, thetas, input_layer_size, hidden_layer_size, num_labels, X, y, reg_lambda):
        t1, t2 = self.unpack_thetas(thetas, input_layer_size, hidden_layer_size, num_labels)

        m = X.shape[0]
        Y = np.eye(num_labels)[y]

        _, _, _, _, h = self._forward(X, t1, t2)
        costPositive = -Y * np.log(h).T
        costNegative = (1 - Y) * np.log(1 - h).T
        cost = costPositive - costNegative
        J = np.sum(cost) / m

        if reg_lambda != 0:
            t1f = t1[:, 1:]
            t2f = t2[:, 1:]
            reg = (self.reg_lambda / (2 * m)) * (self.sumsqr(t1f) + self.sumsqr(t2f))
            J = J + reg
        return J

    def function_prime(self, thetas, input_layer_size, hidden_layer_size, num_labels, X, y, reg_lambda):
        t1, t2 = self.unpack_thetas(thetas, input_layer_size, hidden_layer_size, num_labels)

        m = X.shape[0]
        t1f = t1[:, 1:]
        t2f = t2[:, 1:]
        Y = np.eye(num_labels)[y]

        Delta1, Delta2 = 0, 0
        for i, row in enumerate(X):
            a1, z2, a2, z3, a3 = self._forward(row, t1, t2)

            # Backprop
            d3 = a3 - Y[i, :].T
            d2 = np.dot(t2f.T, d3) * self.activation_func_prime(z2)

            Delta2 += np.dot(d3[np.newaxis].T, a2[np.newaxis])
            Delta1 += np.dot(d2[np.newaxis].T, a1[np.newaxis])

        Theta1_grad = (1 / m) * Delta1
        Theta2_grad = (1 / m) * Delta2

        if reg_lambda != 0:
            Theta1_grad[:, 1:] = Theta1_grad[:, 1:] + (reg_lambda / m) * t1f
            Theta2_grad[:, 1:] = Theta2_grad[:, 1:] + (reg_lambda / m) * t2f

        return self.pack_thetas(Theta1_grad, Theta2_grad)

    def fit(self, X, y):
        num_features = X.shape[0]
        input_layer_size = X.shape[1]
        num_labels = len(set(y))

        theta1_0 = self.rand_init(input_layer_size, self.hidden_layer_size)
        theta2_0 = self.rand_init(self.hidden_layer_size, num_labels)
        thetas0 = self.pack_thetas(theta1_0, theta2_0)

        options = 'maxiter': self.maxiter
        _res = optimize.minimize(self.function, thetas0, jac=self.function_prime, method=self.method, 
                                 args=(input_layer_size, self.hidden_layer_size, num_labels, X, y, 0), options=options)

        self.t1, self.t2 = self.unpack_thetas(_res.x, input_layer_size, self.hidden_layer_size, num_labels)

        np.savetxt("weights_t1.txt", self.t1, newline="\n")
        np.savetxt("weights_t2.txt", self.t2, newline="\n")

    def predict(self, X):
        return self.predict_proba(X).argmax(0)

    def predict_proba(self, X):
        _, _, _, _, h = self._forward(X, self.t1, self.t2)
        return h


##################
# IR data        #
##################
values = np.loadtxt('infrared_data.txt', delimiter=', ', usecols=[0,1,2,3,4])

targets = np.loadtxt('infrared_data.txt', delimiter=', ', dtype=(int), usecols=[5])

X_train, X_test, y_train, y_test = cross_validation.train_test_split(values, targets, test_size=0.4)
nn = NN_1HL()
nn.fit(values, targets)
print("Accuracy of classification: "+str(accuracy_score(y_test, nn.predict(X_test))))

【问题讨论】:

你能打印出训练集的准确率吗?如果(如@goncalopp 建议的那样)您的训练数据集太小,您会看到训练集的准确度很高,但测试集的预测很差。改善这一点的最佳方法是使用更大的训练集,其次是使用更简单的模型(您的数据看起来好像一个简单的 kNN 或线性分类器可能有效;我认为 reg_lambda 是一个“权重衰减”术语,设置> 0 也可能有帮助) @nikie 请注意,反向传播的 FFNN 通常使用随机权重进行初始化。 IIRC,如果学习速度慢,即使在训练完网络之后,训练集中的准确率仍然会很低。 @goncalopp:你的意思是训练会卡在局部最小值?那么训练误差也会很高,这就是我要求它的原因。如果是这种情况,使用更简单的模型(权重衰减、隐藏神经元更少)也应该有所帮助,对吧? @nikie 我添加了以下打印:print("Accuracy of classification on the trainings set: " +str(accuracy_score(targets, nn.predict(values)))) 并根据随机初始化获得 0.6 和 0.8 之间的精度。我将尝试更改正则化参数并减少隐藏层中的节点数。谢谢。 【参考方案1】:

正确规范化数据解决了这个问题。我使用了 sklearn 的预处理模块。这是一个例子:

from sklearn import preprocessing
import numpy as np

X_train = np.array([[ 1., -1.,  2.],
                    [ 2.,  0.,  0.],
                    [ 0.,  1., -1.]])


min_max_scaler = preprocessing.MinMaxScaler()
X_train_minmax = min_max_scaler.fit_transform(X_train)
print(X_train_minmax)

X_test = np.array([[ -3., -1.,  4.]])
X_test_minmax = min_max_scaler.transform(X_test)
print(X_test_minmax)

输出是:

[[ 0.5     0.      1.    ]
 [ 1.      0.5     0.3333]
 [ 0.      1.      0.    ]]


[[-1.5     0.      1.6667]]

【讨论】:

【参考方案2】:

最明显的问题是您的训练数据集非常小

由于您使用的是scipy.optimize.minimize 而不是通常的迭代梯度下降,我认为您也很可能是overfitting your model to your training data。在这里,迭代算法可能效果更好。不要忘记仔细监控验证错误

如果您尝试使用梯度下降进行反向传播,请注意,根据反向传播使用的参数,神经网络需要一段时间才能收敛

您可以尝试多次向网络提供相同的训练数据或调整learning rate,但理想情况下您应该使用更多样化的数据。

【讨论】:

在我的例子中“调整学习率”是什么意思?我不使用梯度下降,而是使用内置优化函数来最小化具有 theta 权重的成本函数。所以基本上我只看到以下要调整的参数:隐藏层中的单元数、最小化函数的最大迭代次数以及避免过度拟合的正则化参数。顺便说一句,如何选择这个正则化参数?什么是合理的值? 我对@9​​87654326@恐怕不是很熟悉。快速查看代码后,您似乎只进行了一次最小化。这可能会导致overfitting 获得训练数据。您需要使用一种逐渐达到所需行为的方法,并仔细监控验证错误,以便在开始过度拟合时立即停止。 Here's typical training/validation fitness curves for a backprop. FFNN 我只是在学习自己。过度拟合意味着假设拟合对于训练数据来说“太好了”并且不能泛化,对吗?但在我的情况下,网络甚至无法对训练样本(只有大约 80%)进行分类,所以我认为这不是过拟合问题。最小化功能也让我感到困惑。它有参数 maxiter ,我可以改变它,但它并没有改善结果。我想我必须找到实现正常梯度下降最小化的方法

以上是关于反向传播神经网络的主要内容,如果未能解决你的问题,请参考以下文章

如何理解CNN神经网络里的反向传播backpropagation,bp算法

007-卷积神经网络-前向传播-反向传播

卷积神经网络:反向传播过程的代码实现

前向传播和反向传播

读懂反向传播算法(bp算法)

神经网络入门——16实现一个反向传播