一、背景

最近一直在做人脸表情的识别,用到的程序是之间的一篇文章中的程序:深度学习(一)——deepNN模型实现摄像头实时识别人脸表情(C++和python3.6混合编程)。这里我只进行了简单的程序修改。

由于该程序是利用fer2013数据集做的,效果不是很好,人脸表情的识别精度仅有70%左右,因此我想自己制作数据集,自己训练模型,关于如何制作数据集,可参考文章:从零开始制作人脸表情的数据集

本文主要介绍在训练模型的过程中出现的问题:即无论训练多少次,其训练精度一直维持在0.23。下面会具体介绍问题及解决办法。

二、问题出现

这里先给出我的代码。首先是关于数据读取的代码,这里给出关键部分代码

def load_data(txt_dir):

    # 省略内容:根据txt的路径读取图像数据和标签    

    data_set = np.empty((count, 128, 128, 1), dtype="float32")    # 定义data_set
    label = np.empty((count,10), dtype="uint8")        # 定义label

    # 省略内容:读取data和标签

    return data_set, label    

然后是deepNN模型的代码,这个完全参考之前的程序,只不过我的图像大小改成了128*128,表情种类为10类:

def deepnn(x):
    x_image = tf.reshape(x, [-1, 128, 128, 1])

    # conv1
    w_conv1 = weight_variables([5, 5, 1, 64])
    b_conv1 = bias_variable([64])
    h_conv1 = tf.nn.relu(conv2d(x_image, w_conv1) + b_conv1)
    h_pool1 = maxpool(h_conv1)
    norm1 = tf.nn.lrn(h_pool1, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)

    # conv2
    w_conv2 = weight_variables([3, 3, 64, 64])
    b_conv2 = bias_variable([64])
    h_conv2 = tf.nn.relu(conv2d(norm1, w_conv2) + b_conv2)
    norm2 = tf.nn.lrn(h_conv2, 4, bias=1.0, alpha=0.001 / 9.0, beta=0.75)
    h_pool2 = maxpool(norm2)

    # Fully connected layer
    w_fc1 = weight_variables([32 * 32 * 64, 384])
    b_fc1 = bias_variable([384])
    h_conv3_flat = tf.reshape(h_pool2, [-1, 32 * 32 * 64])
    h_fc1 = tf.nn.relu(tf.matmul(h_conv3_flat, w_fc1) + b_fc1)

    # Fully connected layer
    w_fc2 = weight_variables([384, 192])
    b_fc2 = bias_variable([192])
    h_fc2 = tf.matmul(h_fc1, w_fc2) + b_fc2

    # linear
    w_fc3 = weight_variables([192, 10])         # 一共10类
    b_fc3 = bias_variable([10])                 # 一共10类
    y_conv = tf.add(tf.matmul(h_fc2, w_fc3), b_fc3)

    return y_conv


def weight_variables(shape):
    initial = tf.truncated_normal(shape, stddev=0.1)
    return tf.Variable(initial)


def bias_variable(shape):
    initial = tf.constant(0.1, shape=shape)
    return tf.Variable(initial)


def conv2d(x, w):
    return tf.nn.conv2d(x, w, strides=[1, 1, 1, 1], padding='SAME')


def maxpool(x):
    return tf.nn.max_pool(x, ksize=[1, 3, 3, 1],
                            strides=[1, 2, 2, 1], padding='SAME')

最后是训练过程的代码,当然这里我根据我的实际情况对原代码进行了修改:

def train_model():

    # 构建模型----------------------------------------------------------
    x = tf.placeholder(tf.float32, [None, 16384])
    y_ = tf.placeholder(tf.float32, [None, 10])

    y_conv = deepnn(x)

    cross_entropy = tf.reduce_mean(
        tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))
    train_step = tf.train.AdamOptimizer(1e-4).minimize(cross_entropy)
    correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))
    accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

    # 构建完毕----------------------------------------------------------

    # 读取数据
    data_set, label = load_data('./data/list.txt')
    max_train_epochs = 30001
    batch_size = 100

    # 判断是否存在输出模型的路径,如果不存在,则创建
    if not os.path.exists('./models/emotion_model'):
        os.makedirs('./models/emotion_model')

    with tf.Session() as sess:
        saver = tf.train.Saver()
        sess.run(tf.global_variables_initializer())

        batch_num = int(data_set.shape[0] / batch_size)

        for i in range(max_train_epochs):
            for j in range(batch_num):
                # 制作每一个batch的图像和标签
                train_image = data_set[j * batch_size:j * batch_size + batch_size]
                train_image = train_image.reshape(-1, 128*128)
                train_label = label[j * batch_size:j * batch_size + batch_size]
                train_label = np.reshape(train_label, [-1, 10])

                # 逐个batch训练模型
                train_step.run(feed_dict={x: train_image, y_: train_label})

            # 每训练一个epoch保存一次精度
            if i % 1 == 0:
                train_accuracy = accuracy.eval(feed_dict={
                    x: train_image, y_: train_label})
                print('epoch %d, training accuracy %f' % (i, train_accuracy))

            # 每1000个epoch保存一次模型
            if i % 1000 == 0:
                saver.save(sess, './models/emotion_model', global_step=i + 1)

好了,现在准备好数据之后,直接运行train_model():

if __name__ == '__main__':
    train_model()

如果不出意外,其每行的输出应该是:

epoch DD, training accuracy FFFFF

且随着训练次数的增加,training accuracy的值也应该是逐渐接近1的。但是实际上的结果:

training accuracy完全没有任何增加的迹象,训练至1000次仍是这样。

三、问题解决

模型不收敛的话,问题出在哪呢?反复排查后确定了模型没有任何问题。那自然只可能是输入数据的问题了。

原来在数据读取过程中,在load_data(txt_dir)函数中,label语句的定义为:

    label = np.empty((count,10), dtype="uint8")

np.empty()函数导致了label中的很多数据是随机产生的,最终的标签结果也并非是0,1二值数据,而是非常混乱的数据:

既然已经查到问题所在了,那么解决方法也自然就明了了。我们的目的是为了产生二值标签,即图像所属的表情类别标记为1,非所属类别标记为0,如此可这样修改上述代码:

def load_data(txt_dir):

    # 省略内容:根据txt的路径读取图像数据和标签    

    # count表示图像的数量
    data_set = np.empty((count, 128, 128, 1), dtype="float32")    # 定义data_set
    label = np.zeros((count,10), dtype="uint8")        # 定义label

    # 省略内容:读取data和标签

    return data_set, label    

修改之后,先看看我们的标签label是否正确:

从上图可以看出,label已经完全没有问题,下来我们再看看训练过程中的training accuracy:

好了,可以看到training accuracy在逐步提高,说明这个问题已完美解决。后续大约在训练60个epoch时,训练精度几乎可以接近1:

 

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐