神经网络训练样本的误差振荡

我实现了一个反向传播神经网络,并用我的数据对其进行了训练。数据在英语和南非荷兰语的句子之间交替。神经网络应该能够识别输入的语言。

网络的结构是27 * 16 * 2,输入层有26个输入,对应字母表的每个字母,另外还有一个偏置单元。

我的问题是,每当遇到新的训练样本时,误差就会剧烈地在相反的方向上波动。正如我提到的,训练样本是以交替的方式读取的(英语,南非荷兰语,英语…)。

我可以训练网络识别所有英语或所有南非荷兰语,但无法在同一轮中识别两种语言中的任何一种(两种都识别)。

下面的y轴是两个输出节点(英语和南非荷兰语)的输出信号误差,x轴是训练样本的数量。从某种意义上说,它确实按照我编程的方式在执行;当样本是英语时,它会调整权重以更好地识别英语。然而,这样做时,它会使网络在预测南非荷兰语时的表现变差。这就是误差在正负值之间波动的原因。

显然,这不是它应该工作的方式,但我被卡住了。

enter image description here

我觉得问题可能出在我对概念的理解上,但这里是相关的代码:

public void train() throws NumberFormatException, IOException{    // 训练准确度
    double at = 0;    // 迭代次数
    int epoch = 0;
    int tNum = 0;
    for(; epoch < epochMax; epoch++){        // 从现有项目中的TestPackage包中读取股票文件
        BufferedReader br = new BufferedReader(new InputStreamReader(this.getClass().
                getResourceAsStream("/TrainingData/" + trainingData.getName())));
        while ((line = br.readLine()) != null) {
            Boolean classified = false;
            tNum++;
            // 设置正确的分类Tk
            t[0] = Integer.parseInt(line.split("\t")[0]); // 南非荷兰语
            t[1] = (t[0] == 0) ? 1 : 0; // 英语
            // 将训练字符串转换为字符数组
            char trainingLine[] = line.split("\t")[1].toLowerCase().toCharArray();
            // 增加输入层z的idx,与字母在字母表中的位置匹配
            // a == 0, b == 2, 等等.....
            for(int l = 0; l < trainingLine.length; l++){
                if((int)trainingLine[l] >= 97 && (int)trainingLine[l] <= 122)
                    z[(int)trainingLine[l] % 97]++;
            }
            /*System.out.println("Z   " + Arrays.toString(z));
            System.out.println();*/
            // 缩放Z
            for(int i = 0; i < z.length-1; i++){
                z[i] = scale(z[i], 0, trainingLine.length, -Math.sqrt(3),Math.sqrt(3));
            }
         /*----------------------------------------------------------------
          *                  设置网络隐藏层
          * 隐藏层的每个第i个单元 =
          * 输入层的每个第i个单元
          * 乘以权重矩阵ij的第i层的每个j*/
            for(int j = 0; j < ij.length; j++){  // 3
                double[] dotProduct = multiplyVectors(z, ij[j]);
                y[j] = sumVector(dotProduct);
               }
            /*----------------------------------------------------------------
             *                 设置隐藏层激活
             */
            for(int j = 0; j < y.length-1; j++){
                y[j] = sigmoid(y[j], .3, .7);
            }
            /*----------------------------------------------------------------
             *                       设置网络输出层
             * 隐藏层的每个第j个单元 =
             * 输入层的每个第j个单元
             * 乘以权重矩阵jk的第j层的每个k*/
            for(int k = 0; k < jk.length; k++){  // 3
                double[] dotProduct = multiplyVectors(y, jk[k]);
                o[k] = sumVector(dotProduct);
            }
            /*----------------------------------------------------------------
             *                   设置输出层激活
             */
            for(int k = 0; k < o.length; k++){
                o[k] = sigmoid(o[k], .3, .7);
            }
            /*----------------------------------------------------------------
             *                     设置输出误差
             * 对于每个训练样本,评估误差。
             * 误差定义为(Tk - Ok)
             * 正确的分类将导致零误差:
             *          (1 - 1) = 0
             *          (0 - 0) = 0
             */
            for(int k = 0; k < o.length; k++){
                oError[k] = t[k] - o[k];
            }
            /*----------------------------------------------------------------
             *                     设置训练准确度
             * 如果误差为0,则1表示成功预测。
             * 如果误差为1,则0表示未成功预测。
             */
            if(quantize(o[0],.3, .7) == t[0] && quantize(o[1], .3, .7) == t[1]){
                classified = true;
                at += 1;
            }
            // 仅在分类错误时计算误差并更改权重
            if(classified){
                continue;
            }
            /*----------------------------------------------------------------
             *                  计算输出信号误差
             *                 ok的误差 = -(tk - ok)(1 - ok)ok
             */
            for(int k = 0; k < o.length; k++){
                oError[k] = outputError(t[k], o[k]);
            }
            /*----------------------------------------------------------------
             *                  计算隐藏层信号误差
             *                               */
            // 术语(1-yk)yk扩展为yk - yk的平方
            // 对于每个第k个输出单元,将其乘以
            // 两个术语(1-yk)yk和jk[k]的总和点积
            for(int j = 0; j < y.length; j++){
                for(int k = 0; k < o.length; k++){
                    /*System.out.println(j+"-"+k);*/
                    yError[j] +=  oError[k] * jk[k][j] * (1 -  y[j]) * y[j];
                }
            }
               /*----------------------------------------------------------------
             *                  计算隐藏-JK-输出层的新权重
             *                               */
            for(int k = 0; k < o.length; k++){
                for(int j = 0; j < y.length; j++){
                    djk[k][j] = (-1*learningRate)*oError[k]*y[j] + momentum*djk[k][j];
                    // 旧权重 = 它们自己 + 新增权重
                    jk[k][j] += djk[k][j];
                 }
            }
            /*----------------------------------------------------------------
             *         计算输入-IJ-隐藏层的新权重
             *                               */
            for(int j = 0; j < y.length-1; j++){
                for(int i = 0; i < z.length; i++){
                    dij[j][i] = (-1*learningRate)*yError[j]*z[i] + momentum*dij[j][i];
                    // 旧权重 = 它们自己 + 新增权重
                    ij[j][i] += dij[j][i];
                 }
            }
        }
    }
    // 准确度百分比
    double at_prec = (at/tNum) * 100;
    System.out.println("训练准确度: " + at_prec);
    }

回答:

我同意评论中提到的,这个模型可能不是你分类问题的最佳选择,但如果你有兴趣尝试让它工作,我会告诉你我认为它为什么会振荡,以及我会如何尝试解决这个问题。

根据我对你的问题和评论的理解,我无法理解网络在这种情况下实际上“学到了”什么。你输入字母(这是句子中字母出现的次数吗?),然后强制它映射到一个输出。假设你现在只使用英语,英语对应于输出1。那么你“训练”它在一个句子上,为了论证起见,它选择字母“a”作为决定性输入,这是一个相当常见的字母。它设置网络权重,使得当它看到“a”时,输出为1,而所有其他字母输入都被加权降低,以至于它们不影响输出。可能不是那么黑白分明,但它可能在做非常相似的事情。现在,每次你输入另一个英语句子时,它只需要看到一个“a”就能给出正确的输出。对仅南非荷兰语作为零输出做同样的事情,它将“a”映射到零。因此,每次你在两种语言之间交替时,它完全重新分配权重…你没有在构建一个结构。误差的反向传播基本上总是固定值,因为没有正确或错误的程度,它要么是这样,要么是那样。所以我预期它会像你看到的那样振荡。

编辑:我认为这归结为使用字母的存在来分类语言类别,并期望两个极端输出之一,而不是关于定义语言的字母之间的关系的任何东西。

在概念层面上,我会有一个完整的预处理阶段来获取一些统计数据。想到的,我可能会计算(我不了解该语言):- 句子中字母“a”与“c”出现的比率- 句子中字母“d”与“p”出现的比率- 句子中单词的平均长度

对每种语言的50个句子执行此操作。一次性输入所有数据,并在整个数据集上进行训练(70%用于训练,15%用于验证,15%用于测试)。你不能每次只在一个值上训练网络(我认为你正在这样做?),它需要看到整个画面。现在你的输出不再那么黑白分明,它有灵活性可以映射到0到1之间的值,而不是每次都是绝对值。任何高于0.5的都是英语,低于0.5的都是南非荷兰语。从10个语言统计参数开始,隐藏层有5个神经元,输出层有1个神经元。

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中创建了一个多类分类项目。该项目可以对…

发表回复

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