TensorFlow2.0教程-自定義訓練實戰(非tf.keras)

TensorFlow2.0教程-自定義訓練實戰(非tf.keras)

本教程我們將使用TensorFlow來實現鳶尾花分類。整個過程包括:構建模型、模型訓練、模型預測。其中,雖然網絡層仍是使用keras.layer的網絡,但訓練過程沒有使用keras的方法,而是使用tensorflow2中eager模式的自動求導方法構造的。

願文地址:https://doit-space.blog.csdn.net/article/details/95041068

最全Tensorflow 2.0 入門教程持續更新:https://blog.csdn.net/qq_31456593/article/details/88606284

完整tensorflow2.0教程代碼請看 https://github.com/czy36mengfei/tensorflow2_tutorials_chinese (歡迎star)

本教程主要由tensorflow2.0官方教程的個人學習復現筆記整理而來,中文講解,方便喜歡閱讀中文教程的朋友,官方教程:https://www.tensorflow.org

導入相關庫

導入TensorFlow和其他所需的Python模塊。 默認情況下,TensorFlow2使用急切執行來程序,會立即返回結果。

from __future__ import absolute_import, division, print_function, unicode_literals
import os
import matplotlib.pyplot as plt
import tensorflow as tf
print('tf version:', tf.__version__)
print('eager execution:', tf.executing_eagerly())
tf version: 2.0.0-alpha0
eager execution: True

鳶尾花分類問題

想象一下,你是一名植物學家,正在尋找一種自動化的方法來對你找到的每種鳶尾花進行分類。 機器學習提供了許多算法來對花進行統計分類。 例如,複雜的機器學習程序可以基於照片對花進行分類。而這裏,我們將根據萼片和花瓣的長度和寬度測量來對鳶尾花進行分類。

鳶尾花有300多種類別,但我們的這裏主要對以下三種進行分類:

  • Iris setosa
  • Iris virginica
  • Iris versicolor
    # [外鏈圖片轉存失敗(img-YiS1cGTo-1562512656938)(https://www.tensorflow.org/images/iris_three_species.jpg)]

幸運的是,有人已經用萼片和花瓣測量創建了120個鳶尾花的數據集。 這是一個流行的初學者機器學習分類問題的經典數據集。

下載數據集
使用tf.keras.utils.get_file函數下載訓練數據集文件。 這將返回下載文件的文件路徑。

train_dataset_url = "https://storage.googleapis.com/download.tensorflow.org/data/iris_training.csv"
train_dataset_fp = tf.keras.utils.get_file(fname=os.path.basename(train_dataset_url),
                                          origin=train_dataset_url)
print('下載數據至:', train_dataset_fp)
下載數據至: /root/.keras/datasets/iris_training.csv

檢查數據

此數據集iris_training.csv是一個純文本文件,用於存儲格式爲逗號分隔值(CSV)的表格數據。 使用head -n5命令在前五個條目中取一個峯值:

!head -n5 {train_dataset_fp}
120,4,setosa,versicolor,virginica
6.4,2.8,5.6,2.2,2
5.0,2.3,3.3,1.0,1
4.9,2.5,4.5,1.7,2
4.9,3.1,1.5,0.1,0

從數據集的此視圖中,請注意以下內容:

第一行是包含有關數據集的信息的標題:
總共有120個例子。 每個示例都有四個特徵和三個可能的標籤名稱之一。
後續行是數據記錄,每行一個示例,其中:
前四個字段是特徵:這些是示例的特徵。 這裏,字段包含代表花卉測量值的浮點數。
最後一列是標籤:這是我們想要預測的值。 對於此數據集,它是與花名稱對應的整數值0,1或2。

column_names = ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species']
# 獲取特徵和標籤名
feature_name = column_names[:-1]
label_name = column_names[-1]

每個標籤都與字符串名稱相關聯(例如,“setosa”),但機器學習通常依賴於數值。使用標籤數字來映射類別,例如:

  • 0:Iris setosa:
  • 1:Iris versicolor
  • 2:Iris virginica
class_names = ['Iris setosa', 'Iris versicolor', 'Iris virginica']

創建一個 tf.data.Dataset

TensorFlow的數據集API處理許多將數據加載到模型中的常見情況。這是一個高級API,用於讀取數據並將其轉換爲用於訓練的數據類型。

由於數據集是CSV格式的文本文件,因此需要使用make_csv_dataset函數將數據解析爲合適的格式。由於此函數爲訓練模型生成數據,因此默認行爲是對數據(shuffle=True, shuffle_buffer_size=10000)進行混洗,並永遠重複數據集(num_epochs=None)。同時還需要設置batch_size參數。

batch_size=32
train_dataset = tf.data.experimental.make_csv_dataset(
    train_dataset_fp,
    batch_size,
    column_names=column_names,
    label_name=label_name,
    num_epochs=1
)

該make_csv_dataset函數返回tf.data.Dataset的(features, label)對,其中features是一個字典:{‘feature_name’: value}

而這些Dataset對象是可迭代的。

features, labels = next(iter(train_dataset))
print(features)
OrderedDict([('sepal_length', <tf.Tensor: id=64, shape=(32,), dtype=float32, numpy=
array([7.6, 6.9, 7.2, 5. , 6.7, 4.8, 5.4, 5.1, 7.7, 6. , 6.3, 7.4, 5.2,
       7.2, 6.7, 6.1, 5. , 4.9, 6.2, 4.5, 6.6, 6. , 5.5, 6.3, 4.8, 6.7,
       6.1, 5.6, 7.3, 6.9, 5.7, 6.3], dtype=float32)>), ('sepal_width', <tf.Tensor: id=65, shape=(32,), dtype=float32, numpy=
array([3. , 3.2, 3.6, 2.3, 3. , 3. , 3.9, 3.7, 3. , 2.2, 2.3, 2.8, 2.7,
       3.2, 3.1, 2.8, 3.4, 3.1, 2.8, 2.3, 3. , 3. , 3.5, 3.3, 3.4, 3. ,
       2.8, 2.9, 2.9, 3.1, 3.8, 2.5], dtype=float32)>), ('petal_length', <tf.Tensor: id=62, shape=(32,), dtype=float32, numpy=
array([6.6, 5.7, 6.1, 3.3, 5.2, 1.4, 1.3, 1.5, 6.1, 5. , 4.4, 6.1, 3.9,
       6. , 5.6, 4. , 1.6, 1.5, 4.8, 1.3, 4.4, 4.8, 1.3, 6. , 1.6, 5. ,
       4.7, 3.6, 6.3, 4.9, 1.7, 5. ], dtype=float32)>), ('petal_width', <tf.Tensor: id=63, shape=(32,), dtype=float32, numpy=
array([2.1, 2.3, 2.5, 1. , 2.3, 0.3, 0.4, 0.4, 2.3, 1.5, 1.3, 1.9, 1.4,
       1.8, 2.4, 1.3, 0.4, 0.1, 1.8, 0.3, 1.4, 1.8, 0.2, 2.5, 0.2, 1.7,
       1.2, 1.3, 1.8, 1.5, 0.3, 1.9], dtype=float32)>)])

相同的特徵被放在同一個數組中,數組維度爲batch_size大小。
可以可視化如圖:

plt.scatter(features['petal_length'],
            features['sepal_length'],
            c=labels,
            cmap='viridis')

plt.xlabel("Petal length")
plt.ylabel("Sepal length")
plt.show()

[外鏈圖片轉存失敗(img-aKWrIZUf-1562512656939)(output_20_0.png)]

一般我們會把同一個數據的不同feature放在同一個數組中,我們使用tf.pack()來將features重構爲(batch_size, num_features)形狀。

def pack_features_vector(features, labels):
    features = tf.stack(list(features.values()), axis=1)
    return features, labels
# 使用tf.data.Dataset.map將重構函數運用到每條數據中。
train_dataset = train_dataset.map(pack_features_vector)
# 查看前5個數據
features, labels = next(iter(train_dataset))
print(features[:5])
tf.Tensor(
[[7.6 3.  6.6 2.1]
 [6.9 3.2 5.7 2.3]
 [7.2 3.6 6.1 2.5]
 [5.  2.3 3.3 1. ]
 [6.7 3.  5.2 2.3]], shape=(5, 4), dtype=float32)

選擇模型

一個模型是功能和標籤之間的關係。對於鳶尾花分類問題,該模型定義了萼片和花瓣測量與預測的鳶尾花物種之間的關係。一些簡單的模型可以用幾行代數來描述,但是複雜的機器學習模型具有很多難以概括的參數。

我們能否在不使用機器學習的情況下確定四種特徵與虹膜物種之間的關係?也就是說,可以使用傳統的編程技術(例如,很多條件語句)來創建模型嗎?也許 - 如果你對數據集進行了足夠長的分析,以確定特定物種的花瓣和萼片測量值之間的關係。這對於更復雜的數據集來說變得困難 - 可能是不可能的。良好的機器學習方法可以爲我們確定模型。如果我們將足夠的代表性示例提供給正確的機器學習模型類型,程序將爲我們找出關係。

選擇具體模型

我們需要選擇要訓練的模型。有很多類型的模型和挑選一個好的經驗。本教程使用神經網絡來解決鳶尾花I分類問題。神經網絡可以找到特徵和標籤之間的複雜關係。它是一個高度結構化的圖,組織成一個或多個隱藏層。每個隱藏層由一個或多個神經元組成。有幾類神經網絡,這個程序使用密集或完全連接的神經網絡:一層中的神經元接收來自每個神經網絡的輸入連接上一層的神經元。例如,圖2說明了一個由輸入層,兩個隱藏層和一個輸出層組成的密集神經網絡:

[外鏈圖片轉存失敗(img-gbcLzJWi-1562512656940)()]
圖2.具有特徵,隱藏層和預測的神經網絡。

當對來自圖2的模型進行訓練並喂入未標記的示例時,它產生三個預測:該花是給定的鳶尾花物種的可能性。這種預測稱爲推理。對於此示例,輸出預測的總和爲1.0。在圖2中,該預測分解爲:0.02對山鳶尾,0.95對於變色鳶尾,並0.03爲錦葵鳶尾。這意味着模型以95%的概率預測 - 未標記的示例花是變色鳶尾。

使用Keras創建模型

TensorFlow tf.keras API是創建模型和圖層的首選方式。這使得構建模型和實驗變得容易,而Keras處理將所有內容連接在一起的複雜性。

該tf.keras.Sequential模型是層的線性堆棧。它的構造函數採用一個層實例列表,在這種情況下,兩個Dense層各有10個節點,一個輸出層有3個節點代表我們的標籤預測。第一層的input_shape參數對應於數據集中的要素數,並且是必需的。

# 構建線性模型
model = tf.keras.Sequential([
    tf.keras.layers.Dense(10, activation='relu',input_shape=(4,)),
    tf.keras.layers.Dense(10, activation='relu'),
    tf.keras.layers.Dense(3)
])

激活函數確定在層中的每個節點的輸出形狀。這些非線性很重要 - 沒有它們,模型將等同於單個層。有許多可用的激活,但ReLU對於隱藏層是常見的。

隱藏層和神經元的理想數量取決於問題和數據集。像機器學習的許多方面一樣,選擇神經網絡的最佳形狀需要知識和實驗經驗。根據經驗,增加隱藏層和神經元的數量通常會創建一個更強大的模型,這需要更多的數據來有效地訓練。

測試模型結構

prediction = model(features)
prediction[:5]
<tf.Tensor: id=229, shape=(5, 3), dtype=float32, numpy=
array([[1.6543204 , 0.12405288, 0.24490094],
       [1.4488522 , 0.11291474, 0.24872684],
       [1.5161525 , 0.11867774, 0.28899187],
       [0.86002606, 0.05858952, 0.06260413],
       [1.3767202 , 0.10884094, 0.21688706]], dtype=float32)>

多分類任務需要使用softmax進行歸一化

tf.nn.softmax(prediction)[:5]
<tf.Tensor: id=235, shape=(5, 3), dtype=float32, numpy=
array([[0.6845738 , 0.148195  , 0.16723117],
       [0.63935834, 0.16809471, 0.19254689],
       [0.6492055 , 0.16049689, 0.1902975 ],
       [0.52654505, 0.23625231, 0.23720267],
       [0.62697244, 0.1764475 , 0.19658   ]], dtype=float32)>

使用tf.argmax獲取概率最大的類標籤

print('prediction:', tf.argmax(prediction, axis=1))
print('label:', labels)
prediction: tf.Tensor([0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0], shape=(32,), dtype=int64)
label: tf.Tensor([2 2 2 1 2 0 0 0 2 2 1 2 1 2 2 1 0 0 2 0 1 2 0 2 0 1 1 1 2 1 0 2], shape=(32,), dtype=int32)

訓練模型

訓練是機器學習中模型從數據集中學習知識並優化自身能力的過程。

Iris分類問題是一個典型的監督學習問題,其通過包含標籤的數據集進行學習。而無監督學習則是僅從特徵中去尋找相應的模式。

訓練和評估的過程都需要計算模型的損失,它可以衡量預測與正確標籤的差距,訓練過程都是要最小化損失。

我們後面將直接使用tf.keras裏面包裝好的損失函數來計算損失。

# 損失函數
loss_object=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

# 獲取損失
def loss(model, x, y):
    y_ = model(x)
    return loss_object(y_true=y, y_pred=y_)
l = loss(model, features, labels)
print(l)
tf.Tensor(1.3738844, shape=(), dtype=float32)

使用tf.GradientTape計算loss對所有變量的梯度。

def grad(model, inputs, targets):
    with tf.GradientTape() as tape:
        loss_value = loss(model, inputs, targets)
    return loss_value, tape.gradient(loss_value, model.trainable_variables)

創建優化器

優化程序將計算出的梯度應用於模型的變量,以最大限度地減少損失函數。 您可以將損失函數視爲曲面(參見圖3),我們希望通過四處走動找到它的最低點。 漸變指向最陡的上升方向 - 所以我們將以相反的方向行進並向下移動。 通過迭代計算每批的損失和梯度,我們將在訓練期間調整模型。 逐漸地,該模型將找到權重和偏差的最佳組合,以最小化損失。 損失越低,模型的預測越好。

[外鏈圖片轉存失敗(img-GoScKPWF-1562512656940)(https://cs231n.github.io/assets/nn3/opt1.gif)]

TensorFlow有許多可用於訓練的優化算法。 該模型使用tf.train.GradientDescentOptimizer實現隨機梯度下降(SGD)算法。 learning_rate設置每次迭代下一步的步長。 這是一個超參數,您通常會調整以獲得更好的結果。

optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)

優化器使用如下

loss_value, grads = grad(model, features, labels)
print('步數:{}, 初始loss值:{}'.format(optimizer.iterations.numpy(),
                                loss_value.numpy()))
optimizer.apply_gradients(zip(grads, model.trainable_variables))
print('步數:{}, loss值:{}'.format(optimizer.iterations.numpy(),
                                loss(model,features, labels).numpy()))
步數:0, 初始loss值:1.3738844394683838
步數:1, loss值:1.1648454666137695

訓練循環

每個epoch數據將會被訓練一次。

# 保存loss和acc
train_loss_results=[]
train_accuracy_results=[]

num_epochs =201
for epoch in range(num_epochs):
    # 用於記錄loss和acc的類
    epoch_loss_avg = tf.keras.metrics.Mean()
    epoch_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()
    
    # 訓練循環
    for x, y in train_dataset:
        # 獲取loss和梯度
        loss_value, grads = grad(model, x, y)
        # 梯度優化
        optimizer.apply_gradients(zip(grads, model.trainable_variables))
        
        # 記錄loss均值
        epoch_loss_avg(loss_value)
        # 記錄準確率
        epoch_accuracy(y, model(x))

    # 保存每個epoch的loss和acc
    train_loss_results.append(epoch_loss_avg.result())
    train_accuracy_results.append(epoch_accuracy.result())

    if epoch % 50 == 0:
        print("Epoch {:03d}: Loss: {:.3f}, Accuracy: {:.3%}".format(epoch,
                                                                    epoch_loss_avg.result(),
                                                                    epoch_accuracy.result()))

Epoch 000: Loss: 1.048, Accuracy: 70.000%
Epoch 050: Loss: 0.074, Accuracy: 99.167%
Epoch 100: Loss: 0.059, Accuracy: 99.167%
Epoch 150: Loss: 0.054, Accuracy: 99.167%
Epoch 200: Loss: 0.051, Accuracy: 99.167%

可視化訓練過程

fig, axes = plt.subplots(2, sharex=True, figsize=(12, 8))
fig.suptitle('Training Metrics')

axes[0].set_ylabel("Loss", fontsize=14)
axes[0].plot(train_loss_results)

axes[1].set_ylabel("Accuracy", fontsize=14)
axes[1].set_xlabel("Epoch", fontsize=14)
axes[1].plot(train_accuracy_results)
plt.show()

[外鏈圖片轉存失敗(img-iCzXXjXv-1562512656941)(output_46_0.png)]

評估模型

評估模型類似於訓練模型。 最大的區別是示例來自單獨的測試集而不是訓練集。 爲了公平地評估模型的有效性,用於評估模型的示例必須與用於訓練模型的示例不同。

測試數據集的設置類似於訓練數據集的設置。 下載CSV文本文件並解析該值,然後將其打亂:

test_url = "https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv"

test_fp = tf.keras.utils.get_file(fname=os.path.basename(test_url),
                                  origin=test_url)
Downloading data from https://storage.googleapis.com/download.tensorflow.org/data/iris_test.csv
8192/573 [============================================================================================================================================================================================================================================================================================================================================================================================================================================] - 0s 0us/step
test_dataset = tf.data.experimental.make_csv_dataset(
    test_fp,
    batch_size,
    column_names=column_names,
    label_name='species',
    num_epochs=1,
    shuffle=False)

test_dataset = test_dataset.map(pack_features_vector)

評估測試數據集上的模型

與訓練階段不同,該模型僅評估測試數據的單個時期。 在下面的代碼單元格中,我們迭代測試集中的每個示例,並將模型的預測與實際標籤進行比較。 這用於測量整個測試集中模型的準確性。

# 準確率統計類
test_accuracy = tf.keras.metrics.Accuracy()

for (x,y) in test_dataset:
    logits = model(x)
    prediction = tf.argmax(logits, axis=1, output_type=tf.int32)
    test_accuracy(prediction, y) 

print('測試集準確率:', test_accuracy.result())
測試集準確率: tf.Tensor(0.96666664, shape=(), dtype=float32)

結果對比

tf.stack([y, prediction], axis=1)
<tf.Tensor: id=164737, shape=(30, 2), dtype=int32, numpy=
array([[1, 1],
       [2, 2],
       [0, 0],
       [1, 1],
       [1, 1],
       [1, 1],
       [0, 0],
       [2, 1],
       [1, 1],
       [2, 2],
       [2, 2],
       [0, 0],
       [2, 2],
       [1, 1],
       [1, 1],
       [0, 0],
       [1, 1],
       [0, 0],
       [0, 0],
       [2, 2],
       [0, 0],
       [1, 1],
       [2, 2],
       [1, 1],
       [1, 1],
       [1, 1],
       [0, 0],
       [1, 1],
       [2, 2],
       [1, 1]], dtype=int32)>

##使用訓練的模型進行預測

我們已經訓練了一個模型並且“證明”它對Iris物種進行分類是好的 - 但不是完美的。 現在讓我們使用訓練有素的模型對未標記的例子做出一些預測; 也就是說,包含特徵但不包含標籤的示例。

在現實生活中,未標記的示例可能來自許多不同的來源,包括應用程序,CSV文件和數據源。 目前,我們將手動提供三個未標記的示例來預測其標籤。 回想一下,標籤號被映射到命名錶示,如下所示:

  • 0: Iris setosa
  • 1: Iris versicolor
  • 2: Iris virginica
predict_dataset = tf.convert_to_tensor([
    [5.1, 3.3, 1.7, 0.5,],
    [5.9, 3.0, 4.2, 1.5,],
    [6.9, 3.1, 5.4, 2.1]
])

predictions = model(predict_dataset)

for i, logits in enumerate(predictions):
  class_idx = tf.argmax(logits).numpy()
  p = tf.nn.softmax(logits)[class_idx]
  name = class_names[class_idx]
  print("Example {} prediction: {} ({:4.1f}%)".format(i, name, 100*p))
Example 0 prediction: Iris setosa (99.9%)
Example 1 prediction: Iris versicolor (99.9%)
Example 2 prediction: Iris virginica (99.1%)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章