python - 二元分类器总是返回 0.5
问题描述
我正在训练一个分类器,它接受一个 RGB 输入(所以三个 0 到 255 值)并返回黑色或白色(0 或 1)字体是否最适合该颜色。训练后,我的分类器总是返回 0.5(或大约)并且永远不会比这更准确。
代码如下:
import tensorflow as tf
import numpy as np
from tqdm import tqdm
print('Creating Datasets:')
x_train = []
y_train = []
for i in tqdm(range(10000)):
x_train.append([np.random.uniform(0, 255), np.random.uniform(0, 255), np.random.uniform(0, 255)])
for elem in tqdm(x_train):
if (((elem[0] + elem[1] + elem[2]) / 3) / 255) > 0.5:
y_train.append(0)
else:
y_train.append(1)
x_train = np.array(x_train)
y_train = np.array(y_train)
graph = tf.Graph()
with graph.as_default():
x = tf.placeholder(tf.float32)
y = tf.placeholder(tf.float32)
w_1 = tf.Variable(tf.random_normal([3, 10], stddev=1.0), tf.float32)
b_1 = tf.Variable(tf.random_normal([10]), tf.float32)
l_1 = tf.sigmoid(tf.matmul(x, w_1) + b_1)
w_2 = tf.Variable(tf.random_normal([10, 10], stddev=1.0), tf.float32)
b_2 = tf.Variable(tf.random_normal([10]), tf.float32)
l_2 = tf.sigmoid(tf.matmul(l_1, w_2) + b_2)
w_3 = tf.Variable(tf.random_normal([10, 5], stddev=1.0), tf.float32)
b_3 = tf.Variable(tf.random_normal([5]), tf.float32)
l_3 = tf.sigmoid(tf.matmul(l_2, w_3) + b_3)
w_4 = tf.Variable(tf.random_normal([5, 1], stddev=1.0), tf.float32)
b_4 = tf.Variable(tf.random_normal([1]), tf.float32)
y_ = tf.sigmoid(tf.matmul(l_3, w_4) + b_4)
loss = tf.reduce_mean(tf.squared_difference(y, y_))
optimizer = tf.train.AdadeltaOptimizer().minimize(loss)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
print('Training:')
for step in tqdm(range(5000)):
index = np.random.randint(0, len(x_train) - 129)
feed_dict = {x : x_train[index:index+128], y : y_train[index:index+128]}
sess.run(optimizer, feed_dict=feed_dict)
if step % 1000 == 0:
print(sess.run([loss], feed_dict=feed_dict))
while True:
inp1 = int(input(''))
inp2 = int(input(''))
inp3 = int(input(''))
print(sess.run(y_, feed_dict={x : [[inp1, inp2, inp3]]}))
如您所见,我首先导入将要使用的模块。接下来我生成我的输入 x 数据集和所需的输出 y 数据集。x_train 数据集由 10000 个随机 RGB 值组成,而 y_train 数据集由 0 和 1 组成,其中 1 对应于均值低于 128 的 RGB 值,0 对应于均值高于 128 的 RGB 值(这确保明亮的背景得到深色字体,反之亦然)。
诚然,我的神经网络过于复杂(或者我假设如此),但据我所知,它是一个非常标准的前馈网络,具有 Adadelta 优化器和默认学习率。
就我有限的知识而言,网络的训练是正常的,但模型总是吐出 0.5。
最后一段代码允许用户输入值并查看它们在传递到神经网络时会变成什么。
我弄乱了不同的激活函数、损失、初始化偏差的方法等。但无济于事。有时当我修改代码时,模型总是分别返回 1 或 0,但这仍然与优柔寡断并一遍又一遍地返回 0.5 一样不准确。我无法在网上找到合适的解决方案来解决我的问题。欢迎任何意见或建议。
编辑:
损失、权重、偏差和输出在训练过程中变化不大(权重和偏差每 1000 次迭代仅变化百分之一和千分之一,损失在 0.3 左右波动)。此外,输出有时会根据输入变化 f(如您所料),但其他时间是恒定的。程序的一次运行导致常量 0.7 作为输出,而另一次总是返回 0.5,除了非常接近于零,它返回 0.3 或 0.4 类型值。上述都不是所需的输出。应该发生的是 (255, 255, 255) 应该映射到 0 并且 (0, 0, 0) 应该映射到 1 并且 (128, 128, 128) 应该映射到 1 或 0,如字体中间颜色真的不重要。
解决方案
通过查看您的网络,我看到了两件事:
- 隐藏层中的 Sigmoid 激活通常是一个糟糕的选择。sigmoid 函数对于大(正或负)输入会饱和,导致梯度在通过网络反向传播时变得越来越小。这通常被称为“梯度消失”问题。可能是输出附近变量的梯度是“健康的”,因此上层正在学习,但是如果下层没有收到任何梯度,它们将简单地继续返回上层无法使用的随机值. 您可以尝试用 eg 替换 sigmoid 激活
tf.nn.relu
。输出层中的 Sigmoid 是可以的(如果您希望输出为 0/1,则有点必要),但是请考虑使用交叉熵而不是平方误差作为损失函数。 - 您的权重初始化可能会导致权重过大。1.0的标准差太高了。这可能会导致数值问题以及使激活更加饱和(因为由于权重较大,您可以期望从一开始就具有较大的激活值)。尝试类似 0.1 的标准,并考虑
truncated_normal
改用它来防止异常值(或使用统一的随机初始化)。
很难说这是否会解决您的问题,但是我相信这两个都是您绝对应该改变的关于您的网络的事情,就像现在一样。
推荐阅读
- error-handling - 预期结构配置,在使用 process::exit 时找到 ()
- javascript - 如何使用 react-router 的 Link 组件链接到我的应用程序之外的 URL?
- python - 在 Python 中使用 for 循环将值分别保存在字典中
- .htaccess - www + http 到 https 转发
- ruby-on-rails - 在不更改表单宽度的情况下在 html 输入中设置 maxlength?
- swift - 如何更改单个 TextField 的键盘语言?
- flutter - 在 Flutter 中根据手机的屏幕尺寸移动/调整内容
- javascript - CSS:根据屏幕大小重新定位列
- android - emitAll 直到另一个操作完成?
- swift - 防止 TextEditor 在 ScrollView 内滚动