1. 程式人生 > >關於訓練深度學習模型deepNN時,訓練精度維持固定值,模型不收斂的解決辦法(tensorflow實現)

關於訓練深度學習模型deepNN時,訓練精度維持固定值,模型不收斂的解決辦法(tensorflow實現)

一、背景

最近一直在做人臉表情的識別,用到的程式是之間的一篇文章中的程式:深度學習(一)——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: