为了让 keras 学习从矩形的二值图像中检测角点,我将问题简化为对 3x3 像素数组进行分类。左上角,像素需要看起来像这样。
[ [0, 0, 0],
[0, 1, 1],
[0, 1, 1] ]
这会生成所有可能的输入形状的完整集合。
def getData():
x = []
y = []
template = numpy.array([[ 0, 0, 0], [0, 1, 1], [0, 1, 1] ])
num = [0, 0, 0, 0, 0, 0, 0, 0, 0]
for i in range(2**9):
n = numpy.array( num ).reshape((3, 3))
x.append( n )
if numpy.all( n == template ):
y.append(1)
else:
y.append(0)
s = 0
j = 0
while s == 0:
if num[j] == 0:
num[j] = 1
s = 1
else:
num[j] = 0
j += 1
if j == len(num):
print(num)
break
return numpy.array(x), numpy.array(y)
我应该能够从简单的单个卷积层中找到分类器。
def createModel():
inp = keras.layers.Input((3, 3, 1))
cnn = keras.layers.Conv2D( 1, (3, 3), activation = None, use_bias=True)(inp)
cnn = keras.layers.Conv2D( 1, (1, 1), activation = "hard_sigmoid")(cnn)
return keras.models.Model(inputs = [inp], outputs=[cnn])
使用这个简单的模型,我可以设置权重并获得我想要的输出。
dw = numpy.array([ -100, -100, -100, -100, 10, 10, -100, 10, 10]).reshape((3, 3, 1, 1))
bw = numpy.array([ -35 ])
ow = numpy.array([ 1 ]).reshape((1, 1, 1, 1))
obw = numpy.array([0])
mdl.set_weights( [dw, bw, ow, obw] )
mdl.compile( loss ="mse",
optimizer=keras.optimizers.Adam(learning_rate=1e-7)
)
mdl.evaluate(x, y)
这会带来以下损失:
16/16 ────────────────────────────── 0s 1ms/步 - 损耗:9.3703e-04
如何训练网络来学习这些权重?
训练网络的基本设置是这样的:
mdl = createModel()
x, y = getData()
mdl.compile( loss ="mse",
optimizer=keras.optimizers.Adam(learning_rate=1e-2)
)
mdl.fit(x, y, epochs=100, verbose=2)
它不起作用,它只是收敛到一个到处都产生 0 的值,考虑到 512 个样本中有 1 个非零,这是合理的。
其他一些观察。
这是该程序的完整版本。
#!/usr/bin/env python3
import keras
import numpy
def createModel():
inp = keras.layers.Input((3, 3, 1))
cnn = keras.layers.Conv2D( 1, (3, 3), activation = None, use_bias=True)(inp)
cnn = keras.layers.Conv2D( 1, (1, 1), activation = "hard_sigmoid")(cnn)
return keras.models.Model(inputs = [inp], outputs=[cnn])
def getData():
x = []
y = []
template = numpy.array([[ 0, 0, 0], [0, 1, 1], [0, 1, 1] ])
num = [0, 0, 0, 0, 0, 0, 0, 0, 0]
for i in range(2**9):
n = numpy.array( num ).reshape((3, 3))
x.append( n )
if numpy.all( n == template ):
y.append(1)
print("found")
else:
y.append(0)
s = 0
j = 0
while s == 0:
if num[j] == 0:
num[j] = 1
s = 1
else:
num[j] = 0
j += 1
if j == len(num):
print(num)
break
return numpy.array(x), numpy.array(y)
mdl = createModel()
x, y = getData()
for ws in mdl.get_weights():
print(ws.shape)
dw = numpy.array([ -100, -100, -100, -100, 10, 10, -100, 10, 10]).reshape((3, 3, 1, 1))
bw = numpy.array([ -35 ])
ow = numpy.array([ 1 ]).reshape((1, 1, 1, 1))
obw = numpy.array([0])
mdl.set_weights( [dw, bw, ow, obw] )
mdl.compile( loss ="mse",
optimizer=keras.optimizers.Adam(learning_rate=1e-7)
)
mdl.evaluate(x, y)
mdl.fit(x, y, epochs=1000, batch_size=32, verbose=2)
t0 = numpy.array([[[ 0, 0, 0], [0, 1, 1], [0, 1, 1] ]])
t1 = numpy.array([[[ 1, 0, 0], [0, 1, 1], [0, 1, 1] ]])
print( mdl(t0) )
print( mdl(t1) )
为了让这个网络学习,我必须稍微改变结构。
def createModel():
inp = keras.layers.Input((3, 3, 1))
cnn = keras.layers.Conv2D( 1, (3, 3), activation = None, use_bias=True)(inp)
cnn = keras.layers.Conv2D( 2, (1, 1), activation = "softmax")(cnn)
op = keras.layers.Reshape((2, ))(cnn)
return keras.models.Model(inputs = [inp], outputs=[op])
现在输出激活使用
softmax
而不是 hard_sigmoid
,重塑层使其能够处理交叉熵损失。
y 数据现在具有两个元素张量而不是单个元素。
y_i = [0, 1] #corner
然后训练
mdl.compile( loss = keras.losses.categorical_crossentropy,
optimizer=keras.optimizers.Adam(learning_rate=1e-3)
)
mdl.evaluate(x, y)
mdl.fit(x, y, epochs=10000, batch_size=512, verbose=2)
这将学习正确的预测,尽管极端情况仍然存在,但到最后会越来越接近。
通过平衡数据集(添加更多极端情况)可以加快训练速度。
我还没有接受这个答案,因为我怀疑我犯了一个更根本的错误。这个问题看起来很简单,因为网络需要花费多少工作来学习。