使用TensorFlow进行多标签文本分类

35

文本数据被组织成具有 20,000 个元素的向量,如 [2, 1, 0, 0, 5, ...., 0]。第 i 个元素表示文本中第 i 个单词的频率。

基准真实标签数据也表示为具有 4,000 个元素的向量,如 [0, 0, 1, 0, 1, ...., 0]。第 i 个元素表示该文本的第 i 个标签是否为正标签。每个文本的标签数量因文本而异。

我有一个用于单标签文本分类的代码。

如何编辑以下代码以进行多标签文本分类?

特别是,我想知道以下几点。

  • 如何使用 TensorFlow 计算准确度。
  • 如何设置阈值来判断标签是否为正或负。例如,如果输出为 [0.80, 0.43, 0.21, 0.01, 0.32],基准真实标签为 [1, 1, 0, 0, 1],则得分超过 0.25 的标签应判断为正。

谢谢。

import tensorflow as tf

# hidden Layer
class HiddenLayer(object):
    def __init__(self, input, n_in, n_out):
        self.input = input

        w_h = tf.Variable(tf.random_normal([n_in, n_out],mean = 0.0,stddev = 0.05))
        b_h = tf.Variable(tf.zeros([n_out]))

        self.w = w_h
        self.b = b_h
        self.params = [self.w, self.b]

    def output(self):
        linarg = tf.matmul(self.input, self.w) + self.b
        self.output = tf.nn.relu(linarg)

        return self.output

# output Layer
class OutputLayer(object):
    def __init__(self, input, n_in, n_out):
        self.input = input

        w_o = tf.Variable(tf.random_normal([n_in, n_out], mean = 0.0, stddev = 0.05))
        b_o = tf.Variable(tf.zeros([n_out]))

        self.w = w_o
        self.b = b_o
        self.params = [self.w, self.b]

    def output(self):
        linarg = tf.matmul(self.input, self.w) + self.b
        self.output = tf.nn.relu(linarg)

        return self.output

# model
def model():
    h_layer = HiddenLayer(input = x, n_in = 20000, n_out = 1000)
    o_layer = OutputLayer(input = h_layer.output(), n_in = 1000, n_out = 4000)

    # loss function
    out = o_layer.output()
    cross_entropy = -tf.reduce_sum(y_*tf.log(out + 1e-9), name='xentropy')    

    # regularization
    l2 = (tf.nn.l2_loss(h_layer.w) + tf.nn.l2_loss(o_layer.w))
    lambda_2 = 0.01

    # compute loss
    loss = cross_entropy + lambda_2 * l2

    # compute accuracy for single label classification task
    correct_pred = tf.equal(tf.argmax(out, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_pred, "float"))

    return loss, accuracy

我认为除了交叉熵之外可能有更好的损失函数可用。 - Aaron
多标签分类问题有许多不同的准确度衡量方法: 单误差率、排名损失、平均精度等等。我自己仍在学习TensorFlow,并尚未成功地实现它们中的任何一种。但或许这篇论文能帮到你:http://arxiv.org/pdf/1312.5419v3.pdf。如果你有任何进展,请告诉我! - Eric Galluzzo
为了更好地了解准确性,可以考虑计算精度和召回率。 - Abhishek Patel
@Benben,y_是什么?我没有看到它被定义。 - SumNeuron
2个回答

20

将输出层的relu改为sigmoid。将交叉熵损失修改为sigmoid交叉熵损失的显式数学公式(在我的情况/版本的tensorflow中显式损失有效)。

import tensorflow as tf

# hidden Layer
class HiddenLayer(object):
    def __init__(self, input, n_in, n_out):
        self.input = input

        w_h = tf.Variable(tf.random_normal([n_in, n_out],mean = 0.0,stddev = 0.05))
        b_h = tf.Variable(tf.zeros([n_out]))

        self.w = w_h
        self.b = b_h
        self.params = [self.w, self.b]

    def output(self):
        linarg = tf.matmul(self.input, self.w) + self.b
        self.output = tf.nn.relu(linarg)

        return self.output

# output Layer
class OutputLayer(object):
    def __init__(self, input, n_in, n_out):
        self.input = input

        w_o = tf.Variable(tf.random_normal([n_in, n_out], mean = 0.0, stddev = 0.05))
        b_o = tf.Variable(tf.zeros([n_out]))

        self.w = w_o
        self.b = b_o
        self.params = [self.w, self.b]

    def output(self):
        linarg = tf.matmul(self.input, self.w) + self.b
        #changed relu to sigmoid
        self.output = tf.nn.sigmoid(linarg)

        return self.output

# model
def model():
    h_layer = HiddenLayer(input = x, n_in = 20000, n_out = 1000)
    o_layer = OutputLayer(input = h_layer.output(), n_in = 1000, n_out = 4000)

    # loss function
    out = o_layer.output()
    # modified cross entropy to explicit mathematical formula of sigmoid cross entropy loss
    cross_entropy = -tf.reduce_sum( (  (y_*tf.log(out + 1e-9)) + ((1-y_) * tf.log(1 - out + 1e-9)) )  , name='xentropy' )    

    # regularization
    l2 = (tf.nn.l2_loss(h_layer.w) + tf.nn.l2_loss(o_layer.w))
    lambda_2 = 0.01

    # compute loss
    loss = cross_entropy + lambda_2 * l2

    # compute accuracy for single label classification task
    correct_pred = tf.equal(tf.argmax(out, 1), tf.argmax(y, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_pred, "float"))

    return loss, accuracy

14

为支持多标签分类,您必须使用交叉熵函数的变体。如果输出少于一千个,则应使用sigmoid_cross_entropy_with_logits,在您的情况下,有4000个输出,您可以考虑候选采样速度比前者更快。

如何使用TensorFlow计算准确性。

这取决于您的问题和想要实现的目标。如果您不想在图像中错过任何对象,则如果分类器全部正确但有一个错了,那么您应该将整个图像视为错误。您还可以认为错过或误分类的对象是一个错误。我认为后者由sigmoid_cross_entropy_with_logits支持。

如何设置判断标签是正面还是负面的阈值。例如,如果输出为[0.80,0.43,0.21,0.01,0.32],而地面实况是[1,1,0,0,1],则得分超过0.25的标签应被视为正面。

阈值是一种方法,您必须决定使用哪种。但是这是某种类型的hack,不是真正的多标签分类。为此,您需要使用我之前提到的函数。


1
我不知道为什么有人建议使用'sigmoid_cross_entropy_with_logits'。如果它的名字所暗示的是-Y*ln(sigmoid(logits)),那么它将通过给每个类别高概率来最小化损失,实际上在我的情况下就是这样。 - Alok Nayak
这个函数不返回概率。我不明白它如何通过给出高值来最小化损失。如果你将你的类设为1,当类不存在时设为0,那么当物体不在图像中时,网络会给出接近于0的值,而当物体在图像中时,会给出接近于1或更大(甚至2或3)的值。我正在使用它,效果非常好。 - jorgemf
我认为你在数学上是错的。应该是:y * ln(sigmoid(logits)) + (1-y) * ln(1-sigmoid(logits)) 所以: logits = 0, y = 0 => 0; logits = 1, y = 1 => 0; logits = 1, y = 0 => 1.3; logits = 0, y = 1 => 1.3;你可以在谷歌上绘制这个函数,并尝试不同的数字。只需要搜索 y * -ln(1 / (1 + e^-x)) + (1-y) * -ln(1-1 / (1 + e^-x))。 - jorgemf
毫无疑问,如果我在方程中替换值,当logits和y不匹配时会出现错误,当它们相同时为0。我的损失定义是毫无疑问的。但在TensorFlow的'sigmoid_cross_entropy_with_logits'中,损失为-loss = -Y*ln(sigmoid(logits))。请证明这种损失的合理性,而不是我使用的损失。 - Alok Nayak
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - jorgemf
显示剩余4条评论

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