你好
我想尝试用简单的逻辑回归进行二元分类。我的数据输出是未标记的,格式为{1,0} //(他/她是否通过考试),成本函数返回(NaN)。哪里出错了?
learning_rate = 0.05total_iterator = 1500display_per = 100data = numpy.loadtxt("ex2data1.txt",dtype=numpy.float32,delimiter=",");training_X = numpy.asarray(data[:,[0,1]]) # 100 x 2
training_X 包含100 x 2的矩阵,作为考试成绩。例如 [98.771 4.817]
training_Y = numpy.asarray(data[:,[2]],dtype=numpy.int) # 100 x 1
training_Y 包含100×1的数组,例如 [1] [0] [0] [1] 由于stackoverflow的格式限制,我无法逐行书写
m = data.shape[0]x_i = tf.placeholder(tf.float32,[None,2]) # None x 2 y_i = tf.placeholder(tf.float32,[None,1]) # None x 1 W = tf.Variable(tf.zeros([2,1])) # 2 x 1 b = tf.Variable(tf.zeros([1])) # 1 x 1 h = tf.nn.softmax(tf.matmul(x_i,W)+b)cost = tf.reduce_sum(tf.add(tf.multiply(y_i,tf.log(h)),tf.multiply(1-y_i,tf.log(1-h)))) / -m
我尝试使用简单的逻辑成本函数,结果返回了’NaN’。我认为我的成本函数完全是垃圾,于是使用了tensorflow示例中的成本函数:
cost = tf.reduce_mean(-tf.reduce_sum(y_i*tf.log(h), reduction_indices=1))
但这也没用。
initializer= tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)init = tf.global_variables_initializer()with tf.Session() as sess: sess.run(init) print("cost: ", sess.run(cost, feed_dict={x_i:training_X, y_i:training_Y}), "w: ", sess.run(W),"b: ", sess.run(b))
回答:
函数 tf.nn.softmax
期望 logits 的最后一个维度(在你的例子中是2,即{1,0})等于类别的数量。由于你的最后一个维度是1,softmax 总是会返回1(因为唯一可用的类别的概率总是1,因为没有其他类别存在)。因此 h
是一个填充了1的张量,tf.log(1-h)
将返回负无穷。大多数情况下,无穷乘以零(在某些行中是 1-y_i
)会返回NaN。
你应该用 tf.nn.sigmoid
替换 tf.nn.softmax
。
一个可能的解决方案是:
h = tf.nn.sigmoid(tf.matmul(x_i,W)+b)cost = tf.reduce_sum(tf.add(tf.multiply(y_i,tf.log(h)),tf.multiply(1-y_i,tf.log(1-h)))) / -m
或者更好的是,你可以使用 tf.sigmoid_cross_entropy_with_logits
在这种情况下,应该这样做:
h = tf.matmul(x_i,W)+bcost = tf.reduce_mean(tf.sigmoid_cross_entropy_with_logits(labels=y_i, logits=h))
这个函数在数值上比使用 tf.nn.sigmoid
后跟随交叉熵函数更稳定,后者在 tf.nn.sigmoid
接近0或1时可能会因为 float32
的精度问题返回NaN。