训练神经网络进行加法

23

我需要训练一个网络来对两个输入进行加法或乘法,但是在20000次迭代后看起来它无法逼近所有点。更具体地说,我将其训练整个数据集,并且它逼近了最后几个点,但似乎对于前面的点没有任何改进。我将数据归一化到-0.8和0.8之间。这个网络本身由2个输入、3个隐藏神经元和1个输出神经元组成。我还将网络的学习率设置为0.25,并使用tanh(x)作为学习函数。

它对于在数据集中最后被训练的点逼近得非常好,但对于最早的点来说似乎无法逼近得很好。我想知道是什么原因导致它不能良好地调整,是我使用的拓扑结构,还是其他问题?

此外,对于该网络,隐藏层中有多少个神经元是合适的?


据我所知,神经元具有二进制输出,它要么“触发”,要么不触发。如果默认情况下输出为1或0,您打算如何获得像加法或乘法这样的输出? - dStulle
6
不,那只是一种神经元的类型(尽管是常见的一种),你谈论的。 - Kos
5个回答

9

可能有些晚了,但一个简单的解决方案是使用RNN循环神经网络)。

RNN SUM TWO DIGITS

在将数字转换为数字后,您的NN将从左到右的数字序列中取出几个数字。
RNN必须循环其输出之一,以便自动理解存在进位数字(如果总和为2,则写入0并进位1)。
要对其进行训练,您需要提供由两个数字组成的输入(一个来自第一个数字,另一个来自第二个数字)和所需的输出。然后,RNN最终会找到如何进行求和。
请注意,此RNN仅需要了解以下8种情况即可学习如何将两个数字相加:
1 + 1、0 + 0、1 + 0、0 + 1(有进位) 1 + 1、0 + 0、1 + 0、0 + 1(无进位)

9

一个由单个神经元组成的网络,权重为{1,1},偏置为0,采用线性激活函数,可以执行两个输入数字的加法。

乘法可能更难。这里有两种方法可以使用神经网络:

  1. 将其中一个数字转换为数字(例如,二进制),并像在小学时一样执行乘法。a*b = a*(b0*2^0 + b1*2^1 + ... + bk*2^k) = a*b0*2^0 + a*b1*2^1 + ... + a*bk*2^k。这种方法很简单,但需要数量可变的神经元,与输入b的长度(对数)成比例。
  2. 取输入的对数,将它们相加,然后指数化结果。a*b = exp(ln(a) + ln(b)) 只要可以足够好地近似对数和指数,这个网络就可以处理任意长度的数字。

3
如果你想保持神经网络的中性(链接有权重,神经元通过权重计算输入的加权和并根据sigmoid函数的结果回答0或1,使用梯度反向传播),那么你应该将隐藏层的神经元视为分类器。它们定义了一个将输入空间分成类别的线条:一个类别对应于神经元响应1的部分,另一个类别对应于神经元响应0的部分。隐藏层的第二个神经元将定义另一个分隔线,以此类推。输出神经元通过调整其权重,使其输出与学习期间呈现的输出相对应,结合隐藏层的输出。
因此,单个神经元将在2个类别中分类输入空间(可能对应于加法,具体取决于学习数据库)。两个神经元可以定义4个类别。三个神经元8个类别,等等。将隐藏神经元的输出视为2的幂:h1*2^0 + h2*2^1+...+hn*2^n,其中hi是隐藏神经元i的输出。注意:您需要n个输出神经元。这回答了关于使用隐藏神经元数量的问题。
但是,神经网络不会计算加法。它将其视为基于其学习的分类问题。它将无法为超出其学习基础的值生成正确答案。在学习阶段,它会调整权重,以便放置分隔线(2D中的线)以产生正确答案。如果您的输入在[0,10]之间,则它将学习为[0,10]^2中的值的加法生成正确答案,但对于12 + 11不会给出好的答案。
如果您的最后一个值已经学得很好,而第一个值被遗忘,请尝试降低学习率:最后几个示例的权重修改可能会覆盖第一个示例(如果您正在使用随机反向传播)。确保您的学习基础是公平的。您还可以更频繁地呈现学习不良的示例。并尝试许多学习率的值,直到找到一个好的值。

神经网络(NN)用于解决分类问题,而加法不是分类问题 - 这是您在回答中的意思吗? - lowtech
是的。在一般情况下,它可以被看作是一个针对一组受限数字(例如{0,1} ;-))的分类问题,但在一般情况下,它不是这样的。 - jcm69

1
我试图做同样的事情。训练了2、3、4位数的加法,能够达到97%的准确率。你可以通过其中一种神经网络类型来实现, 用神经网络进行序列到序列学习 使用keras的Juypter Notebook提供了一个示例程序,可在以下链接中找到。

https://github.com/keras-team/keras/blob/master/examples/addition_rnn.py

希望这有所帮助。 附上代码以供参考。
from __future__ import print_function
from keras.models import Sequential
from keras import layers
import numpy as np
from six.moves import range


class CharacterTable(object):
    """Given a set of characters:
    + Encode them to a one hot integer representation
    + Decode the one hot integer representation to their character output
    + Decode a vector of probabilities to their character output
    """
    def __init__(self, chars):
        """Initialize character table.
        # Arguments
            chars: Characters that can appear in the input.
        """
        self.chars = sorted(set(chars))
        self.char_indices = dict((c, i) for i, c in enumerate(self.chars))
        self.indices_char = dict((i, c) for i, c in enumerate(self.chars))

    def encode(self, C, num_rows):
        """One hot encode given string C.
        # Arguments
            num_rows: Number of rows in the returned one hot encoding. This is
                used to keep the # of rows for each data the same.
        """
        x = np.zeros((num_rows, len(self.chars)))
        for i, c in enumerate(C):
            x[i, self.char_indices[c]] = 1
        return x

    def decode(self, x, calc_argmax=True):
        if calc_argmax:
            x = x.argmax(axis=-1)
        return ''.join(self.indices_char[x] for x in x)


class colors:
    ok = '\033[92m'
    fail = '\033[91m'
    close = '\033[0m'

# Parameters for the model and dataset.
TRAINING_SIZE = 50000
DIGITS = 3
INVERT = True

# Maximum length of input is 'int + int' (e.g., '345+678'). Maximum length of
# int is DIGITS.
MAXLEN = DIGITS + 1 + DIGITS

# All the numbers, plus sign and space for padding.
chars = '0123456789+ '
ctable = CharacterTable(chars)

questions = []
expected = []
seen = set()
print('Generating data...')
while len(questions) < TRAINING_SIZE:
    f = lambda: int(''.join(np.random.choice(list('0123456789'))
                    for i in range(np.random.randint(1, DIGITS + 1))))
    a, b = f(), f()
    # Skip any addition questions we've already seen
    # Also skip any such that x+Y == Y+x (hence the sorting).
    key = tuple(sorted((a, b)))
    if key in seen:
        continue
    seen.add(key)
    # Pad the data with spaces such that it is always MAXLEN.
    q = '{}+{}'.format(a, b)
    query = q + ' ' * (MAXLEN - len(q))
    ans = str(a + b)
    # Answers can be of maximum size DIGITS + 1.
    ans += ' ' * (DIGITS + 1 - len(ans))
    if INVERT:
        # Reverse the query, e.g., '12+345  ' becomes '  543+21'. (Note the
        # space used for padding.)
        query = query[::-1]
    questions.append(query)
    expected.append(ans)
print('Total addition questions:', len(questions))

print('Vectorization...')
x = np.zeros((len(questions), MAXLEN, len(chars)), dtype=np.bool)
y = np.zeros((len(questions), DIGITS + 1, len(chars)), dtype=np.bool)
for i, sentence in enumerate(questions):
    x[i] = ctable.encode(sentence, MAXLEN)
for i, sentence in enumerate(expected):
    y[i] = ctable.encode(sentence, DIGITS + 1)

# Shuffle (x, y) in unison as the later parts of x will almost all be larger
# digits.
indices = np.arange(len(y))
np.random.shuffle(indices)
x = x[indices]
y = y[indices]

# Explicitly set apart 10% for validation data that we never train over.
split_at = len(x) - len(x) // 10
(x_train, x_val) = x[:split_at], x[split_at:]
(y_train, y_val) = y[:split_at], y[split_at:]

print('Training Data:')
print(x_train.shape)
print(y_train.shape)

print('Validation Data:')
print(x_val.shape)
print(y_val.shape)

# Try replacing GRU, or SimpleRNN.
RNN = layers.LSTM
HIDDEN_SIZE = 128
BATCH_SIZE = 128
LAYERS = 1

print('Build model...')
model = Sequential()
# "Encode" the input sequence using an RNN, producing an output of HIDDEN_SIZE.
# Note: In a situation where your input sequences have a variable length,
# use input_shape=(None, num_feature).
model.add(RNN(HIDDEN_SIZE, input_shape=(MAXLEN, len(chars))))
# As the decoder RNN's input, repeatedly provide with the last hidden state of
# RNN for each time step. Repeat 'DIGITS + 1' times as that's the maximum
# length of output, e.g., when DIGITS=3, max output is 999+999=1998.
model.add(layers.RepeatVector(DIGITS + 1))
# The decoder RNN could be multiple layers stacked or a single layer.
for _ in range(LAYERS):
    # By setting return_sequences to True, return not only the last output but
    # all the outputs so far in the form of (num_samples, timesteps,
    # output_dim). This is necessary as TimeDistributed in the below expects
    # the first dimension to be the timesteps.
    model.add(RNN(HIDDEN_SIZE, return_sequences=True))

# Apply a dense layer to the every temporal slice of an input. For each of step
# of the output sequence, decide which character should be chosen.
model.add(layers.TimeDistributed(layers.Dense(len(chars))))
model.add(layers.Activation('softmax'))
model.compile(loss='categorical_crossentropy',
              optimizer='adam',
              metrics=['accuracy'])
model.summary()

# Train the model each generation and show predictions against the validation
# dataset.
for iteration in range(1, 200):
    print()
    print('-' * 50)
    print('Iteration', iteration)
    model.fit(x_train, y_train,
              batch_size=BATCH_SIZE,
              epochs=1,
              validation_data=(x_val, y_val))
    # Select 10 samples from the validation set at random so we can visualize
    # errors.
    for i in range(10):
        ind = np.random.randint(0, len(x_val))
        rowx, rowy = x_val[np.array([ind])], y_val[np.array([ind])]
        preds = model.predict_classes(rowx, verbose=0)
        q = ctable.decode(rowx[0])
        correct = ctable.decode(rowy[0])
        guess = ctable.decode(preds[0], calc_argmax=False)
        print('Q', q[::-1] if INVERT else q, end=' ')
        print('T', correct, end=' ')
        if correct == guess:
            print(colors.ok + '☑' + colors.close, end=' ')
        else:
            print(colors.fail + '☒' + colors.close, end=' ')
        print(guess)

-3

想象一下,如果您将tanh(x)阈值函数替换为x的线性函数 - 称之为a.x - 并将a视为每个神经元中唯一的学习参数,那么会发生什么。这实际上是您的网络将优化的内容; 它是tanh函数的零交叉点的近似。

现在,当您层叠这种线性类型的神经元时会发生什么?随着脉冲从输入到输出,您将乘以每个神经元的输出。您正在尝试使用一组乘法来近似加法。正如他们所说,这是不可计算的。


5
我认为你的数学有问题。没有非线性激活函数,多层人工神经网络就会崩溃成一个单层网络,这只是将输入向量乘以权重矩阵。每个输出都只是输入的加权。一个具有1权重和0偏置的单个神经元/感知器对两个输入求和。 - Ark-kun

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接