【问题标题】:What's wrong with my implementation of Neural Networks?我的神经网络实现有什么问题?
【发布时间】:2018-02-05 17:48:34
【问题描述】:

我想绘制神经网络相对于训练样例数量的学习误差曲线。这是代码:

import sklearn
import numpy as np
from sklearn.model_selection import learning_curve
import matplotlib.pyplot as plt
from sklearn import neural_network
from sklearn import cross_validation

myList=[]
myList2=[]
w=[]

dataset=np.loadtxt("data", delimiter=",")
X=dataset[:, 0:6]
Y=dataset[:,6]
clf=sklearn.neural_network.MLPClassifier(hidden_layer_sizes=(2,3),activation='tanh')

# split the data between training and testing
X_train, X_test, Y_train, Y_test = cross_validation.train_test_split(X, Y, test_size=0.25, random_state=33)

# begin with few training datas
X_eff=X_train[0:int(len(X_train)/150), : ]
Y_eff=Y_train[0:int(len(Y_train)/150)]

k=int(len(X_train)/150)-1


for m in range (140) :


    print (m)

    w.append(k)

    # train the model and store the training error
    A=clf.fit(X_eff,Y_eff)
    myList.append(1-A.score(X_eff,Y_eff))

      # compute the testing error
    myList2.append(1-A.score(X_test,Y_test))

    # add some more training datas
    X_eff=np.vstack((X_eff,X_train[k+1:k+101,:]))
    Y_eff=np.hstack((Y_eff,Y_train[k+1:k+101]))
    k=k+100

plt.figure(figsize=(8, 8))
plt.subplots_adjust()
plt.title("Erreur d'entrainement et de test")
plt.plot(w,myList,label="training error")
plt.plot(w,myList2,label="test error")
plt.legend()
plt.show()

但是,我得到了一个非常奇怪的结果,曲线波动,训练误差非常接近测试误差,这似乎并不正常。 错误在哪里?我不明白为什么会有这么多的起伏,为什么训练错误没有像预期的那样增加。任何帮助将不胜感激!

编辑:我使用的数据集是https://archive.ics.uci.edu/ml/datasets/Chess+%28King-Rook+vs.+King%29,在那里我删除了少于 1000 个实例的类。我手动重新编码了乱码数据。

【问题讨论】:

  • 你逐渐扩大训练集有什么原因吗?
  • @Flomp 这就是绘制学习曲线的方式。
  • 没有实际数据很难说。您是否尝试过调整 MLP 的参数,例如隐藏层或激活函数。 tanh IMO 通常给出这种曲线。也许尝试改变它。在其位置尝试“relu”或“logistic”
  • 很抱歉,我对此了解不多。使用其他激活函数时结果是否不同?
  • 您为此打开了赏金,这意味着您对答案不满意(没关系),但两个答案都直接回答了所述问题。如果人们不知道您在寻找什么,赏金将无济于事。您介意解释为什么提供的答案不符合要求吗?我很想调整其中一个以使其适合您。

标签: python scikit-learn neural-network


【解决方案1】:

我认为您看到这种曲线的原因是您衡量的性能指标与您优化的性能指标不同。

优化指标

神经网络最小化损失函数,在 tanh 激活的情况下,我假设您使用的是交叉熵损失的修改版本。如果您要绘制随时间变化的损失,您会看到一个更单调递减的误差函数,如您所料。 (实际上并不是单调的,因为神经网络是非凸的,但这不是重点。)

性能指标

您测量的性能指标是准确度百分比,它不同于损失。为什么这些不同?损失函数以可微分的方式告诉我们有多少误差(这对于快速优化方法很重要)。准确度指标告诉我们预测的好坏,这对于神经网络的应用很有用。

把它放在一起

因为您正在绘制相关指标的性能,您可以预期该图看起来与您的优化指标相似。但是,由于它们不相同,您可能会在您的情节中引入一些无法解释的差异(正如您发布的情节所证明的那样)。

有几种方法可以解决此问题。

  1. 绘制损失而不是准确度。如果您确实需要准确度图,这实际上并不能解决您的问题,但它会为您提供更平滑的曲线。
  2. 绘制多次运行的平均值。保存算法的 20 次独立运行的准确度图(如训练网络 20 次),然后将它们平均在一起并绘制它。这将大大减少方差。

TL;DR

不要期望准确度图总是平滑且单调递减,它不会。


问题编辑后:

现在您已经添加了数据集,我看到了其他一些可能导致您遇到的问题的事情。

量级信息

数据集定义了几个棋子的等级和文件(行和列)。这些是作为从 1 到 6 的整数输入的。但是 2 真的 1 比 1 好吗? 6真的4比2好吗?就棋位而言,我认为情况并非如此。

想象一下,我正在构建一个以金钱为输入的分类器。我的价值观是否刻画了一些信息?是的,1 美元与 100 美元完全不同;我们可以根据大小判断出存在关系。

对于国际象棋游戏,第 1 行的含义是否与第 8 行不同?一点也不,事实上这些尺寸是对称的!在您的网络中使用偏置单元可以通过“重新调整”您的输入以有效地从 [-3, 4] (现在以 0 为中心(ish)左右)来帮助解释对称性。

解决方案

但是,我认为,您可以通过平铺编码或一次性编码您的每个功能获得最大的收益。不要让网络依赖于包含在每个特征量级中的信息,因为这可能会导致网络进入糟糕的局部最优状态。

【讨论】:

  • 好吧,我没想到会有一个单调的函数,但这里的方差似乎确实很高。此外,我用 SVM 做了类似的曲线,在那里,我得到了非常平滑的单调曲线。此外,我感兴趣的实际上是准确性(或错误),因为我有兴趣将其与 VC 界限进行比较
  • 但是,感谢您对准确性和损失之间差异的解释。 (如果我很好理解,准确性是一种“分类好坏”的衡量标准,实用而损失更“数学”)。所以+1,但我不能接受你的回答,因为它不符合我的要求
  • 也许我不清楚当时的要求。如果你能把问题说得更清楚,也许你就能得到你想要的答案。
  • SVM 和 ANN 的优化之间存在很多差异,我并不惊讶 SVM 的曲线更平滑。代码中肯定有一些错误导致了这种情况,但对我来说最明显的问题是不能保证 ANN 准确度曲线是平滑的。事实上,它很少是平滑的。
  • 感谢您的回答,在这种情况下,一个热编码似乎确实是一个关键点......您是否有一个正确的例子来说明如何做到这一点,因为我并不真正相信Scikit-Learn?
【解决方案2】:

随机化训练集并重复

如果您想公平比较训练样本数量对准确性的影响,我建议从您的训练集中随机选择n_samples,而不是在前一批中添加 100 个样本。您还可以为每个 n_samples 值重复拟合 N_repeat 次。

这会给出这样的结果(未测试):

n_samples_array = np.arange(100,len(X_train),100)
N_repeat = 10

for n_samples in n_samples_array:
    print(n_samples)
    
    # repeat the fit several times and take the mean
    myList_tmp, myList2_tmp = [],[]
    for repeat in range(0,N_repeat):
        # Randomly pick samples
        selection = np.random.choice(range(0,len(X_train)),n_samples,repeat=False)
    
        # train the model and store the training error
        A=clf.fit(X_train[selection],Y_train[selection])
        myList_tmp.append(1-A.score(X_train[selection],Y_train[selection]))

          # compute the testing error
        myList2_tmp.append(1-A.score(X_test,Y_test))
    
    myList.append(np.mean(myList_tmp))
    myList2.append(np.mean(myList2_tmp))

热启动

当您使用fit 函数时,您将从头开始优化。如果您希望在将一些样本添加到相同的先前训练的网络时看到优化的改进,您可以使用选项warm_start=True

根据documentation

warm_start : bool,可选,默认为 False

当设置为 True 时,重用之前调用 fit 作为初始化的解决方案,否则,只需擦除之前的解决方案。

【讨论】:

  • 感谢您的回答!然而,虽然我的测试误差在减少(这很好),但我的训练误差也在减少,这不正常,是吗?
  • 如果您的训练样本较少,您往往会过拟合,因此训练误差可能非常低。但是,您的网络非常小(2 个隐藏层,每个隐藏层分别有 2 个和 3 个神经元)。很难说没有看到数据(例如在散点图上)。考虑到神经元数量少且预测精度低(r-square),如果网络没有真正学到任何东西,我不会感到惊讶。
  • 实际上,我上次的模拟是在一个包含 10 个隐藏层的神经网络上完成的,每个隐藏层有 100 个神经元
  • @MysteryGuy 我明白了,所以对于少于 15k 的数据点,您基本上有超过 100k 的免费参数。您是否尝试过warm_start 选项,它可能有助于平滑您的曲线。另外,我建议使用另一个激活函数。 relu是目前深度学习中使用最广泛的。由于梯度消失问题,Sigmoid 的学习速度非常慢。
【解决方案3】:

除了前面的答案,您还应该记住,您可能需要调整网络的learning rate(通过在初始化程序中设置learning_rate = value)。如果您选择较大的速率,您将从局部最小值跳到另一个或围绕这些点转圈,但实际上不会收敛(见下图,取自 here)。

此外,还请绘制loss 而不仅仅是您网络的准确性。这将使您对它有更好的了解。

另外,请记住,您必须使用大量训练和测试数据来获得或多或少“平滑”的曲线,甚至是具有代表性的曲线;如果您只使用几个(可能几百个)数据点,则生成的指标实际上不会很准确,因为它们包含很多随机的东西。要解决此错误,您不应每次都使用相同的示例训练网络,而应更改训练数据的顺序,并可能将其拆分为不同的 mini batches。我非常有信心,您可以通过尝试关注这些方面并实施它们来解决甚至减少您的问题。

根据您的问题类型,您应该将激活函数更改为与tanh 函数不同的东西。执行分类时,OneHotEncoder 也可能有用(如果您的数据尚未经过热编码); sklearn 框架也提供了一个 implementation

【讨论】:

  • 我尝试了relu 激活函数,正如许多其他架构所建议的那样,但曲线与我之前的曲线相似
  • @MysteryGuy 你也降低了学习率吗?您是否尝试过使用OneHotEncoder?或许您可以将数据集提供给我们,以便我们自己尝试。
  • 关于小批量,除了stackoverflow.com/questions/38157972/…,还有其他实现吗?
  • 我的意思是,用 sklearn 可以直接处理吗?
  • @MysteryGuy 我不知道MLPClassifier 正在内部执行此操作。因此,您可能会尝试在初始化时将batch_size 设置为更小的值(例如 64)。此外,您也可以通过将 max_iter 设置为更高的值来增加 epoch 的总数。
猜你喜欢
  • 1970-01-01
  • 2011-05-18
  • 2016-02-19
  • 2011-02-22
  • 1970-01-01
  • 2014-01-17
  • 1970-01-01
  • 2016-12-23
  • 2017-05-27
相关资源
最近更新 更多