深入理解TensorFlow中的tf.metrics運算元

劉美利發表於2018-08-24

01 概述

本文將深入介紹Tensorflow內建的評估指標運算元,以避免出現令人頭疼的問題。

  • tf.metrics.accuracy()

  • tf.metrics.precision()

  • tf.metrics.recall()

  • tf.metrics.mean_iou()

簡單起見,本文在示例中使用tf.metrics.accuracy(),但它的模式以及它背後的原理將適用於所有評估指標。如果您只想看到有關如何使用tf.metrics的示例程式碼,請跳轉到5.1和5.2節,如果您想要了解為何使用這種方式,請繼續閱讀。

這篇文章將通過一個非常簡單的程式碼示例來理解tf.metrics

的原理,這裡使用Numpy建立自己的評估指標。這將有助於對Tensorflow中的評估指標如何工作有一個很好的直覺認識。然後,我們將給出如何採用tf.metrics快速實現同樣的功能。但首先,我先講述一下寫下這篇部落格的由來。

02 背景

這篇文章的由來是來自於我嘗試使用tf.metrics.mean_iou評估指標進行影像分割,但卻獲得完全奇怪和不正確的結果。我花了一天半的時間來弄清楚我哪裡出錯了。你會發現,自己可能會非常容易錯誤地使用tf的評估指標。截至2017年9月11日,tensorflow文件並沒有非常清楚地介紹如何正確使用Tensorflow的評估指標。

因此,這篇文章旨在幫助其他人避免同樣的錯誤,並且深入理解其背後的原理,以便了解如何正確地使用它們。

03 生成資料

在我們開始使用任何評估指標之前,讓我們先從簡單的資料開始。我們將使用以下Numpy陣列作為我們預測的標籤和真實標籤。陣列的每一行視為一個batch,因此這個例子中共有4個batch。
import numpy as np
labels = np.array([[1,1,1,0],
                   [1,1,1,0],
                   [1,1,1,0],
                   [1,1,1,0]], dtype=np.uint8)
predictions = np.array([[1,0,0,0],
                        [1,1,0,0],
                        [1,1,1,0],
                        [0,1,1,1]], dtype=np.uint8)
n_batches = len(labels)

04 建立評價指標

為了簡單起見,這裡採用的評估指標是準確度(accuracy):

如果我們想計算整個資料集上的accuracy,可以這樣計算:

n_items = labels.size
accuracy = (labels ==  predictions).sum() / n_items
print("Accuracy :", accuracy)
[OUTPUT]
Accuracy : 0.6875

這種方法的問題在於它不能擴充套件到大型資料集,這些資料集太大而無法一次性載入到記憶體。為了使其可擴充套件,我們希望使評估指標能夠逐步更新,每次更新一個batch中預測值和標籤。為此,我們需要跟蹤兩個值:

  • 正確預測的例子總和 

  • 目前所有例子的總數

在Python中,我們建立兩個全域性變數:

# Initialize running variables
N_CORRECT = 0
N_ITEMS_SEEN = 0

每次新來一個batch,我們將這個batch中的預測情況更新到這兩個變數中:

# Update running variables
N_CORRECT += (batch_labels == batch_predictions).sum()
N_ITEMS_SEEN += batch_labels.size

而且,我們可以實時地計算每個點處的accuracy:

# Calculate accuracy on updated values
acc = float(N_CORRECT) / N_ITEMS_SEEN

合併前面的功能,我們建立如下的程式碼:

# Create running variables
N_CORRECT = 0
N_ITEMS_SEEN = 0
def reset_running_variables():
    """ Resets the previous values of running variables to zero     """
    global N_CORRECT, N_ITEMS_SEEN
    N_CORRECT = 0
    N_ITEMS_SEEN = 0
def update_running_variables(labs, preds):
    global N_CORRECT, N_ITEMS_SEEN
    N_CORRECT += (labs == preds).sum()
    N_ITEMS_SEEN += labs.size
def calculate_accuracy():
    global N_CORRECT, N_ITEMS_SEEN
    return float(N_CORRECT) / N_ITEMS_SEEN

4.1 整體accuracy

使用上面的函式,當我們便利完所有的batch之後,可以計算出整體accuracy:

reset_running_variables()
for i in range(n_batches):
    update_running_variables(labs=labels[i], preds=predictions[i])
accuracy = calculate_accuracy()
print("[NP] SCORE: ", accuracy)
[OUTPUT]
[NP] SCORE:  0.6875

4.2 每個batch的accuracy

但是,如果我們想要計算每個batch的accuracy,那就要重新組織我們的程式碼了。每次更新全域性變數之前,你需要先重置它們(歸為0):

for i in range(n_batches):
    reset_running_variables()
    update_running_variables(labs=labels[i], preds=predictions[i])
    acc = calculate_accuracy()
    print("- [NP] batch {} score: {}".format(i, acc))
[OUTPUT]
- [NP] batch 0 score: 0.5
- [NP] batch 1 score: 0.75
- [NP] batch 2 score: 1.0
- [NP] batch 3 score: 0.5

05 Tensorflow中的metrics

在第4節中我們將計算評估指標的操作拆分為不同函式,這其實與Tensorflow中tf.metrics背後原理是一樣的。當我們呼叫tf.metrics.accuracy函式時,類似的事情會發生:

  • 會同樣地建立兩個變數(變數會加入tf.GraphKeys.LOCAL_VARIABLES集合中),並將其放入幕後的計算圖中:

    total(相當於N_CORRECT)

    count(相當於N_ITEMS_SEEN)

  • 返回兩個tensorflow操作。

    accuracy(相當於calculate_accuracy())

    update_op(相當於update_running_variables())

為了初始化和重置變數,比如第4節中的reset_running_variables函式,我們首先需要獲得這些變數(total和count)。你可以在第一次呼叫時為tf.metrics.accuracy函式顯式指定一個名稱,比如:

tf.metrics.accuracy(label, prediction, name="my_metric")

然後就可以根據作用範圍找到隱式建立的2個變數:

# Isolate the variables stored behind the scenes by the metric operation
running_vars = tf.get_collection(tf.GraphKeys.LOCAL_VARIABLES, scope="my_metric")
<tf.Variable 'my_metric/total:0' shape=() dtype=float32_ref>,
<tf.Variable 'my_metric/count:0' shape=() dtype=float32_ref>

接下了我們可以建立一個初始化操作,以可以初始化或者重置兩個變數:

running_vars_initializer = tf.variables_initializer(var_list=running_vars)

當你需要初始化或者重置變數時,只需要在session中執行一下即可:

session.run(running_vars_initializer)

注意:除了手動分離變數,然後建立初始化op,在TF中更常用的是下面的操作:

session.run(tf.local_variables_initializer())

所以,有時候你看到上面的操作不要大驚小怪,其實只是初始化了在tf.GraphKeys.LOCAL_VARIABLES集合中的變數,但是這樣做把所以變數都初始化了,使用時要特別注意。

知道上面的東西,我們很容易計算整體accuracy和batch中的accuracy。

5.1 計算整體accuracy

在TF中要計算整體accuracy,只需要如此:

import tensorflow as tf
graph = tf.Graph()
with graph.as_default():
    # Placeholders to take in batches onf data
    tf_label = tf.placeholder(dtype=tf.int32, shape=[None])
    tf_prediction = tf.placeholder(dtype=tf.int32, shape=[None])
    # Define the metric and update operations
    tf_metric, tf_metric_update = tf.metrics.accuracy(tf_label,
                                                      tf_prediction,
                                                      name="my_metric")
    # Isolate the variables stored behind the scenes by the metric operation
    running_vars = tf.get_collection(tf.GraphKeys.LOCAL_VARIABLES, scope="my_metric")
    # Define initializer to initialize/reset running variables
    running_vars_initializer = tf.variables_initializer(var_list=running_vars)
with tf.Session(graph=graph) as session:
    session.run(tf.global_variables_initializer())
    # initialize/reset the running variables
    session.run(running_vars_initializer)
    for i in range(n_batches):
        # Update the running variables on new batch of samples
        feed_dict={tf_label: labels[i], tf_prediction: predictions[i]}
        session.run(tf_metric_update, feed_dict=feed_dict)
    # Calculate the score
    score = session.run(tf_metric)
    print("[TF] SCORE: ", score)
[OUTPUT]
[TF] SCORE:  0.6875

5.2 計算每個batch的accuracy

為了分別計算各個batch的準確度,在每批新資料之前將變數重置為零:

with tf.Session(graph=graph) as session:
    session.run(tf.global_variables_initializer())
    for i in range(n_batches):
        # Reset the running variables
        session.run(running_vars_initializer)
        # Update the running variables on new batch of samples
        feed_dict={tf_label: labels[i], tf_prediction: predictions[i]}
        session.run(tf_metric_update, feed_dict=feed_dict)
        # Calculate the score on this batch
        score = session.run(tf_metric)
        print("[TF] batch {} score: {}".format(i, score))
[OUTPUT]
[TF] batch 0 score: 0.5
[TF] batch 1 score: 0.75
[TF] batch 2 score: 1.0
[TF] batch 3 score: 0.5

注意:如果每個batch計算之前不重置變數的話,其實計算的累積accuracy,就是目前已經執行資料的accuracy。

5.3 要避免的問題

不要在相同的session.run()中同時執行tf_metrics和tf_metric_update,比如這樣:

_ , score = session.run([tf_metric_update, tf_metric], feed_dict=feed_dict)
score, _ = session.run([tf_metric, tf_metric_update], feed_dict=feed_dict)

在Tensorflow 1.3 (或許其它版本)中,這可能得到不一致的結果。這兩個op,update_op才是真正負責更新變數,而第一個op只是簡單根據當前變數計算評價指標,所以你應該先執行update_op,然後再用第一個op計算指標。需要注意的,update_op執行後一個作用是更新變數,另外會同時返回一個結果,對於tf.metric.accuracy,就是更新變數後實時計算的accuracy。

06 其它metrics

tf.metrics中的其他評估指標將以相同的方式工作。它們之間的唯一區別可能是呼叫tf.metrics函式時需要額外引數。例如,tf.metrics.mean_iou需要額外的引數num_classes來表示預測的類別數。另一個區別是背後所建立的變數,如tf.metrics.mean_iou建立的是一個混淆矩陣,但仍然可以按照我在本文第5部分中描述的方式收集和初始化它們。

07 結語

對於TF中所有metric,其都是返回兩個op,一個是計算評價指標的op,另外一個是更新op,這個op才是真正其更新作用的。我想之所以TF會採用這種方式,是因為metric所服務的其實是評估模型的時候,此時你需要收集整個資料集上的預測結果,然後計算整體指標,而TF的metric這種設計恰好滿足這種需求。但是在訓練模型時使用它們,就是理解它的原理,才可以得到正確的結果。

【本文轉載自:機器學習演算法工程師,作者:葉虎,原文連結:https://mp.weixin.qq.com/s/8I5Nvw4t2jT1NR9vIYT5XA】

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31542119/viewspace-2212800/,如需轉載,請註明出處,否則將追究法律責任。

相關文章