我是一位经验丰富的Python开发人员,但是机器学习方面的新手。这是我第一次使用Keras。你能告诉我我在做什么错吗?
我正在尝试制作一个采用二进制形式的数字的神经网络,并在除以7时输出其模。(我的目标是要做一个非常简单的任务,以确保一切正常。)
在下面的代码中,我定义了网络,并在10,000个随机数上对其进行训练。然后,我用500个随机数对其进行了测试。
由于某种原因,我获得的准确度约为1/7,这是您希望从完全随机算法获得的准确度,即我的神经网络没有做任何事情。
谁能帮助我找出问题所在?
import keras.models
import numpy as np
from python_toolbox import random_tools
RADIX = 7
def _get_number(vector):
return sum(x * 2 ** i for i, x in enumerate(vector))
def _get_mod_result(vector):
return _get_number(vector) % RADIX
def _number_to_vector(number):
binary_string = bin(number)[2:]
if len(binary_string) > 20:
raise NotImplementedError
bits = (((0,) * (20 - len(binary_string))) +
tuple(map(int, binary_string)))[::-1]
assert len(bits) == 20
return np.c_[bits]
def get_mod_result_vector(vector):
return _number_to_vector(_get_mod_result(vector))
def main():
model = keras.models.Sequential(
(
keras.layers.Dense(
units=20, activation='relu', input_dim=20
),
keras.layers.Dense(
units=20, activation='relu'
),
keras.layers.Dense(
units=20, activation='softmax'
)
)
)
model.compile(optimizer='sgd',
loss='categorical_crossentropy',
metrics=['accuracy'])
data = np.random.randint(2, size=(10000, 20))
labels = np.vstack(map(get_mod_result_vector, data))
model.fit(data, labels, epochs=10, batch_size=50)
def predict(number):
foo = model.predict(_number_to_vector(number))
return _get_number(tuple(map(round, foo[0])))
def is_correct_for_number(x):
return bool(predict(x) == x % RADIX)
predict(7)
sample = random_tools.shuffled(range(2 ** 20))[:500]
print('Total accuracy:')
print(sum(map(is_correct_for_number, sample)) / len(sample))
print(f'(Accuracy of random algorithm is {1/RADIX:.2f}')
if __name__ == '__main__':
main()
答案 0 :(得分:4)
UPD
经过一番修补,我能够使用RNN获得一个相当不错的解决方案。它只训练所有可能的唯一输入的5%以下,并在随机测试样本上提供> 90%的精度。您可以将批次数量从40增加到100,以使其更加准确(尽管在某些运行中,模型可能无法收敛到正确的答案-这比平时要高)。我在这里改用Adam优化器,不得不将样本数量增加到50K(10K导致我过度拟合)。
请理解,此解决方案有点麻烦,因为它基于任务域知识,可以通过对输入位序列(甚至是输入位)的简单重复公式来定义目标函数如果您反转输入的位序列,则可以使用更简单的公式,但是在LSTM中使用go_backwards=True
并没有帮助。
如果您反转输入位的顺序(以便我们总是从最高有效位开始),则目标函数的重复公式仅为F_n = G(F_{n-1}, x_n)
,其中F_n = MOD([x_1,...,x_n], 7)
和{{1} }-只有49个不同的输入和7个可能的输出。因此,该模型必须学习初始状态+此G(x, y) = MOD(2*x+y, 7)
更新功能。对于从最低有效位开始的序列,重复公式会稍微复杂一些,因为它还需要跟踪每个步骤上当前的G
,但是看来这对于训练来说并不重要。
请注意-这些公式仅用于解释RNN在这里起作用的原因。下面的网络只是一个普通的LSTM层+ softmax,原始的位输入被视为一个序列。
使用RNN层的答案的完整代码:
MOD(2**n, 7)
原始答案
我不确定它是如何发生的,但是对于NN,您选择检查代码的特定任务非常困难。我认为最好的解释是,当要素以这样的方式互连时,NN并不是很好,即改变一个要素总是会完全改变目标输出的值。一种看待它的方法是在期望得到某个答案时查看特征集-在您的情况下,它们看起来像是在20维空间中的大量平行超平面的并集-并且对于这7个类别中的每一个,这些集合的平面“很好地”交错,并留给NN进行区分。
也就是说-如果您的示例数量很大,例如10K,可能的输入数量较小,则说您的输入位数只有8位大(因此只能有256个唯一输入)-网络应该“学习”正确的功能还不错(通过“记住”每个输入的正确答案,而无需泛化)。对于您而言,这种情况不会发生,因为代码具有以下错误。
您的标签是20维向量,其位为0-6整数(您实际需要的标签)-因此,我想您很想教NN作为单独的分类器来学习答案的位(以前只有3位)可能非零)。我将其更改为我想像的您真正想要的-长度为7的向量,只有一个值为1,其他为0(所谓的一种热编码,keras实际上根据this期望import keras.models
import numpy as np
from python_toolbox import random_tools
RADIX = 7
FEATURE_BITS = 20
def _get_number(vector):
return sum(x * 2 ** i for i, x in enumerate(vector))
def _get_mod_result(vector):
return _get_number(vector) % RADIX
def _number_to_vector(number):
binary_string = bin(number)[2:]
if len(binary_string) > FEATURE_BITS:
raise NotImplementedError
bits = (((0,) * (FEATURE_BITS - len(binary_string))) +
tuple(map(int, binary_string)))[::-1]
assert len(bits) == FEATURE_BITS
return np.c_[bits]
def get_mod_result_vector(vector):
v = np.repeat(0, 7)
v[_get_mod_result(vector)] = 1
return v
def main():
model = keras.models.Sequential(
(
keras.layers.Reshape(
(1, -1)
),
keras.layers.LSTM(
units=100,
),
keras.layers.Dense(
units=7, activation='softmax'
)
)
)
model.compile(optimizer=keras.optimizers.Adam(learning_rate=0.01),
loss='categorical_crossentropy',
metrics=['accuracy'])
data = np.random.randint(2, size=(50000, FEATURE_BITS))
labels = np.vstack(map(get_mod_result_vector, data))
model.fit(data, labels, epochs=40, batch_size=50)
def predict(number):
foo = model.predict(_number_to_vector(number))
return np.argmax(foo)
def is_correct_for_number(x):
return bool(predict(x) == x % RADIX)
sample = random_tools.shuffled(range(2 ** FEATURE_BITS))[:500]
print('Total accuracy:')
print(sum(map(is_correct_for_number, sample)) / len(sample))
print(f'(Accuracy of random algorithm is {1/RADIX:.2f}')
if __name__ == '__main__':
main()
使用)。如果您想尝试分别学习每一位,那么您绝对不应该在最后一层中使用softmax 20,因为这样的输出会在20个类中生成总计为1的概率(在这种情况下,您应该训练20个或以上-3个二进制分类器)。由于您的代码没有为keras提供正确的输入,因此最终得到的模型是随机的,并且对您应用的舍入方法旨在为95%-100%的输入输出相同的值。
下面的代码稍作更改即可训练一个模型,该模型可以或多或少地正确猜出0到255的每个数字的mod 7答案(再次,几乎记住每个输入的正确答案)。如果您尝试增加categorical_crossentropy
,则结果会大大降低。如果您实际上想训练NN来学习20位或更多位输入的任务(并且不向NN提供所有可能的输入和无限的训练时间),则需要应用一些特定于任务的特征转换和/或一些精心设计的层完全适合您要完成的任务,就像其他人在对问题的评论中已经提到的那样。
FEATURE_BITS
答案 1 :(得分:3)
这实现了99.74%的准确度和99.69%的验证准确度。
import tensorflow as tf, numpy as np
def int2bits(i,fill=20):
return list(map(int,bin(i)[2:].zfill(fill)))
def bits2int(b):
return sum(i*2**n for n,i in enumerate(reversed(b)))
# Data.
I = np.random.randint(0,2**20,size=(250_000,))
X = np.array(list(map(int2bits,I)))
Y = np.array([int2bits(2**i,7) for i in I % 7])
# Test Data.
It = np.random.randint(0,2**20,size=(10_000,))
Xt = np.array(list(map(int2bits,It)))
Yt = np.array([int2bits(2**i,7) for i in It % 7])
# Model.
model = tf.keras.models.Sequential([
tf.keras.layers.Dense(1000,'relu'),
tf.keras.layers.Dense(7,'softmax'),
])
model.compile('adam','categorical_crossentropy',['accuracy'])
# Train.
model.fit(X,Y,10_000,100,validation_data=(Xt,Yt))
一些要点:
1)您的数据太少了。您从0到2 ** 20均匀地采样点,但是仅采样了10,000,这仅是模型假设要学习的向量的1%。关键是很多组件(以二进制表示)将大部分固定为零或一个,而没有任何机会了解它们在整体数据中的功能或与其他组件的交互方式。
2)您需要一个嵌入层,即将空间扩展到更高的质量,以便神经元更容易移动。这使学习可以更好地洗牌,希望找到您想要的算法。单个Dense(1000)似乎有效。
3)批处理10_000(仅是为了使CPU使用率最大化)。跑了100个纪元。将我的validation_data包含在训练中,这样我就可以看到验证集在每个时期的执行情况(包括这不会影响训练,只是在训练时可以更轻松地查看模型是否运行良好)。
谢谢。 :-)