深度學習模型訓練的效率提升,與硬體資源運用息息相關。批次大小的調整,需考量GPU記憶體容量,過大或過小皆非最佳解。Keras層的運用,能將預處理步驟向量化,避免逐張影像處理的效能損耗。TensorFlow圖形內運算,能減少CPU與GPU間的資料傳輸,提升整體效能。必要時,可使用tf.py_function()整合Python程式碼。模型的匯出與載入,SavedModel格式為TensorFlow生態系的推薦選擇,也能透過工具轉換為ONNX格式,提升跨平台相容性。簡化的模型呼叫介面,能提升使用者經驗,方便整合至應用系統。
訓練流程最佳化:批次處理與圖形運算最佳化
在深度學習模型的訓練過程中,批次處理(batching)是提高訓練效率的關鍵因素之一。適當的批次大小不僅能提升運算效能,也能充分利用GPU的記憶體資源。
批次大小對訓練效能的影響
實驗結果(見表7-4)顯示,適當增加批次大小可以顯著提升訓練速度,但這種改善效果會隨著批次大小的增加而遞減。過大的批次大小甚至可能導致GPU記憶體不足的問題。
| 批次大小 | CPU 時間 | 實際時間 |
|---|---|---|
| 1 | 11.4 秒 | 8.09 秒 |
| 8 | 9.56 秒 | 6.90 秒 |
| 16 | 9.90 秒 | 6.70 秒 |
| 32 | 9.68 秒 | 6.37 秒 |
為何使用Keras層進行預處理
在第六章中,我們將隨機翻轉、色彩扭曲等預處理步驟實作為Keras層,這樣的設計有其必要性。假設我們使用map()函式來進行色彩扭曲:
trainds = tf.data.TFRecordDataset(
[filename for filename in tf.io.gfile.glob(pattern)]
).map(preproc.read_from_tfr).map(_preproc_img_label
).map(color_distort).batch(32)
其中color_distort()函式實作如下:
def color_distort(image, label):
contrast = np.random.uniform(0.5, 1.5)
brightness = np.random.uniform(-0.2, 0.2)
image = tf.image.adjust_contrast(image, contrast)
image = tf.image.adjust_brightness(image, brightness)
image = tf.clip_by_value(image, 0, 1)
return image, label
內容解密:
- 色彩扭曲函式實作:該函式隨機調整影像的對比度和亮度,以增強模型的泛化能力。
- 效率考量:直接在
map()中使用色彩扭曲會導致逐一處理影像,大幅降低效率。 - 最佳實踐:將預處理步驟實作為Keras層,可以在批次層級上進行處理,顯著提升效能。
正確的做法是將預處理步驟放在Keras層中,這樣不僅能提高運算效率,也便於在推理流程中重現相同的預處理邏輯。
保持運算在TensorFlow圖形內
TensorFlow的設計理念是在GPU上執行數學運算以提升效率。因此,將資料從CPU傳輸到GPU後,所有屬於tf.data管線的程式碼都應在GPU上執行。同樣地,Keras模型層的運算也應在GPU上進行。
圖形內運算的重要性
- 避免資料傳輸開銷:直接在GPU上處理資料可以避免不必要的資料傳輸。
- 效能最佳化:將所有運算保持在TensorFlow圖形內,可以最大化利用GPU的運算能力。
使用tf.py_function()呼叫純Python程式碼
在某些情況下,我們需要呼叫純Python程式碼來完成特定的任務,例如時區轉換或JSON資料解析。這時可以使用tf.py_function()來實作:
def to_grayscale(img):
return tf.py_function(to_grayscale_numpy, [img], tf.float32)
內容解密:
tf.py_function()的作用:該函式允許我們在TensorFlow圖形中呼叫純Python函式。- 引數說明:需要指定要包裝的函式名稱、輸入張量和輸出資料型別。
- 使用場景:當需要使用Python特定的函式庫(如
pytz或json)時,可以透過此方法實作。
這種方法使得我們可以在保持TensorFlow圖形運算效率的同時,利用Python生態系統中的豐富資源。
TensorFlow 影像處理最佳化技術
在深度學習的應用中,影像處理是一項重要的任務。TensorFlow 提供了一系列的 API 來處理影像資料。本文將探討如何使用 TensorFlow 來最佳化影像處理的效能。
將影像轉換為灰階
首先,我們來看看如何將一張彩色影像轉換為灰階影像。原始的做法是使用 NumPy 來進行轉換,但是這樣的做法效率不高。
使用 NumPy 轉換
def to_grayscale_numpy(img):
img = img.numpy()
rows, cols, _ = img.shape
result = np.zeros([rows, cols], dtype=np.float32)
# ...
return tf.convert_to_tensor(result)
使用 TensorFlow 的 Slicing 功能
def to_grayscale(img):
red = img[:, :, 0]
green = img[:, :, 1]
blue = img[:, :, 2]
c_linear = 0.2126 * red + 0.7152 * green + 0.0722 * blue
gray = tf.where(c_linear > 0.0031308,
1.055 * tf.pow(c_linear, 1 / 2.4) - 0.055,
12.92 * c_linear)
return gray
程式碼解析
在這個程式碼中,我們使用了 TensorFlow 的 Slicing 功能來取出影像的紅、綠、藍三個通道。然後,我們使用 tf.where 來進行條件判斷,將 c_linear 的值轉換為灰階值。
red = img[:, :, 0]:取出影像的紅色通道。green = img[:, :, 1]:取出影像的綠色通道。blue = img[:, :, 2]:取出影像的藍色通道。c_linear = 0.2126 * red + 0.7152 * green + 0.0722 * blue:計算c_linear的值。gray = tf.where(c_linear > 0.0031308, 1.055 * tf.pow(c_linear, 1 / 2.4) - 0.055, 12.92 * c_linear):使用tf.where來進行條件判斷,將c_linear的值轉換為灰階值。
矩陣運算最佳化
我們可以進一步最佳化 c_linear 的計算,使用矩陣運算來取代 Slicing。
def to_grayscale(img):
wt = tf.constant([[0.2126], [0.7152], [0.0722]])
c_linear = tf.matmul(img, wt)
gray = tf.where(c_linear > 0.0031308,
1.055 * tf.pow(c_linear, 1 / 2.4) - 0.055,
12.92 * c_linear)
return gray
程式碼解析
在這個程式碼中,我們使用了矩陣運算來計算 c_linear 的值。
wt = tf.constant([[0.2126], [0.7152], [0.0722]]):建立一個常數矩陣wt。c_linear = tf.matmul(img, wt):使用矩陣運算來計算c_linear的值。
分批處理
我們可以將多張影像一起處理,以提高效率。
class Grayscale(tf.keras.layers.Layer):
def __init__(self, **kwargs):
super(Grayscale, self).__init__(kwargs)
def call(self, img):
wt = tf.constant([[0.2126], [0.7152], [0.0722]])
c_linear = tf.matmul(img, wt)
gray = tf.where(c_linear > 0.0031308,
1.055 * tf.pow(c_linear, 1 / 2.4) - 0.055,
12.92 * c_linear)
return gray
程式碼解析
在這個程式碼中,我們定義了一個自定義的 Keras Layer,用於將多張影像轉換為灰階。
wt = tf.constant([[0.2126], [0.7152], [0.0722]]):建立一個常數矩陣wt。c_linear = tf.matmul(img, wt):使用矩陣運算來計算c_linear的值。gray = tf.where(c_linear > 0.0031308, 1.055 * tf.pow(c_linear, 1 / 2.4) - 0.055, 12.92 * c_linear):使用tf.where來進行條件判斷,將c_linear的值轉換為灰階值。
儲存模型狀態
在訓練完成後,我們需要儲存模型的狀態,以便後續的使用。
儲存模型狀態的原因有兩個:
- 進行推斷:儲存模型狀態可以讓我們在後續的使用中,直接載入模型進行推斷。
- 繼續訓練:儲存模型狀態可以讓我們在後續的使用中,繼續訓練模型。
preproc_model = tf.keras.Sequential([
Grayscale(input_shape=(336, 600, 3)),
tf.keras.layers.Lambda(lambda gray: tf.reduce_mean(gray, axis=[1, 2]))
])
程式碼解析
在這個程式碼中,我們定義了一個 Sequential Model,用於將影像轉換為灰階並計算平均值。
Grayscale(input_shape=(336, 600, 3)):建立一個 Grayscale Layer,用於將影像轉換為灰階。tf.keras.layers.Lambda(lambda gray: tf.reduce_mean(gray, axis=[1, 2])):建立一個 Lambda Layer,用於計算灰階影像的平均值。
模型匯出與載入:TensorFlow SavedModel 格式詳解
在機器學習模型的開發與佈署過程中,模型的儲存與匯出是至關重要的步驟。TensorFlow 提供了 SavedModel 格式,這是一種用於儲存和載入模型的方式,特別是在模型佈署和線上預測的場景中。本文將探討 TensorFlow 中的模型匯出、載入及其相關技術細節。
匯出模型
匯出模型是將訓練好的 Keras 模型儲存為 SavedModel 格式的過程。使用 save() 方法可以輕鬆實作這一點:
os.mkdir('export')
model.save('export/flowers_model')
在 export/flowers_model 目錄下,將會產生一個名為 saved_model.pb 的 protobuf 檔案、變數權重以及模型所需的其他資產檔案,如詞彙表等。
程式碼解析:
os.mkdir('export'): 建立一個名為export的目錄,用於存放匯出的模型。model.save('export/flowers_model'): 將訓練好的 Keras 模型儲存到指定路徑,採用 SavedModel 格式。
SavedModel 與 ONNX 格式的比較
除了 SavedModel 格式,ONNX(Open Neural Network Exchange)也是一種開放原始碼、框架無關的機器學習模型格式。可以使用 tf2onnx 工具將 TensorFlow 模型轉換為 ONNX 格式,以實作跨框架的相容性。
載入與呼叫模型
可以使用 TensorFlow 提供的 saved_model_cli 命令列工具來檢查 SavedModel 的內容:
saved_model_cli show --tag_set all --dir export/flowers_model
這條命令會顯示模型的輸入輸出簽名,例如:
inputs['random/center_crop_input'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 448, 448, 3)
name: serving_default_random/center_crop_input:0
outputs['flower_prob'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 5)
name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict
程式碼解析:
saved_model_cli show: 用於顯示 SavedModel 的詳細資訊。--tag_set all: 指定顯示所有標籤集的資訊。--dir export/flowers_model: 指定 SavedModel 的存放路徑。
TensorFlow 函式簽名
TensorFlow 中的函式簽名包含函式名稱、輸入引數及其型別、輸出型別等資訊。為了使模型能夠被正確呼叫,需要明確指定輸入輸出的形狀和型別。
@tf.function(input_signature=[
tf.TensorSpec([3,5], name='a'),
tf.TensorSpec([5,8], name='b')
])
def myfunc(a, b):
return tf.matmul(a, b)
程式碼解析:
@tf.function: 將 Python 函式轉換為 TensorFlow 圖函式。input_signature: 指定輸入引數的形狀和型別。tf.TensorSpec: 用於定義張量的形狀和型別。
簡化模型呼叫介面
為了讓客戶端更容易使用模型,可以簡化模型的呼叫介面。例如,將輸入從張量改為 JPEG 檔案,並傳回更易理解的預測結果:
@tf.function(input_signature=[tf.TensorSpec([None,], dtype=tf.string)])
def predict_flower_type(filenames):
# 省略實作細節
return {
'probability': top_prob,
'flower_type_int': pred_label_index,
'flower_type_str': pred_label
}
程式碼解析:
input_signature=[tf.TensorSpec([None,], dtype=tf.string)]: 指定輸入為字串張量,用於接收 JPEG 檔案的路徑。return: 傳回一個字典,包含預測的機率、類別索引以及類別名稱。
提升模型效率與儲存狀態的最佳實踐
在前述章節中,我們已經探討瞭如何將模型匯出以進行推論(inference)。本章節將進一步討論如何提升模型的效率以及儲存模型的狀態,以便於繼續訓練。
向量化輸入以提升效率
為了提升模型的效率,我們可以將多個輸入影像組合成批次(batch),一次性進行預測。這種向量化的做法不僅在訓練階段有益,在預測階段同樣能帶來效率的提升。
給定一個檔案名稱的列表,我們可以透過以下方式取得輸入影像:
input_images = [create_preproc_image(f) for f in filenames]
然而,這種做法需要在加速的 TensorFlow 程式碼和未加速的 Python 程式碼之間來回傳遞資料。若我們擁有一個包含檔案名稱的張量(tensor),可以使用 tf.map_fn() 在 TensorFlow 圖形內實作迭代的效果,從而保持所有資料在 TensorFlow 圖形內。
使用 tf.map_fn() 實作向量化輸入
input_images = tf.map_fn(
create_preproc_image,
filenames,
fn_output_signature=tf.float32
)
接著,呼叫模型以取得完整的機率矩陣(probability matrix):
batch_pred = model(input_images)
然後,找到最大機率及其對應的索引:
top_prob = tf.math.reduce_max(batch_pred, axis=1)
pred_label_index = tf.math.argmax(batch_pred, axis=1)
最後,透過 tf.gather() 將預測的標籤索引對映到實際的標籤名稱:
pred_label = tf.gather(params=tf.convert_to_tensor(CLASS_NAMES), indices=pred_label_index)
內容解密:
tf.map_fn():用於在 TensorFlow 圖形內對張量的元素進行迭代操作,避免了 Python 和 TensorFlow 之間的資料傳遞。tf.math.reduce_max()和tf.math.argmax():分別用於計算最大機率和找到最大機率對應的索引。指定axis=1是因為我們是在批次(batch)內進行操作。tf.gather():用於根據索引從張量中收集元素,將預測的標籤索引對映到實際的類別名稱。
儲存模型狀態
在訓練過程中,儲存模型狀態(checkpointing)是非常重要的,不僅可以在訓練結束時儲存模型,也可以在訓練過程中定期儲存,以便於從中斷的地方繼續訓練。
使用 Keras 的 ModelCheckpoint 回撥函式
model_checkpoint_cb = tf.keras.callbacks.ModelCheckpoint(
filepath='./chkpts',
monitor='val_accuracy', mode='max',
save_best_only=True
)
history = model.fit(train_dataset,
validation_data=eval_dataset,
epochs=NUM_EPOCHS,
callbacks=[model_checkpoint_cb])
這裡,我們設定了在驗證準確率(val_accuracy)最高的情況下儲存模型。
結合早停法(Early Stopping)
early_stopping_cb = tf.keras.callbacks.EarlyStopping(
monitor='val_accuracy', mode='max',
patience=2
)
callbacks = [model_checkpoint_cb, early_stopping_cb]
早停法可以在驗證準確率連續多個 epoch 沒有提升時自動停止訓練。
分散式策略(Distribution Strategy)
為了在多個處理器、加速器或機器上分佈模型的訓練,需要使用分散式策略。TensorFlow 提供了多種分散式策略,例如 MirroredStrategy,可以簡單地透過以下方式使用:
strategy = tf.distribute.MirroredStrategy()
with strategy.scope():
layers = [...]
model = tf.keras.Sequential(layers)
model.compile(...)
history = model.fit(...)
內容解密:
tf.distribute.MirroredStrategy():建立一個映象分散式策略,用於在多個 GPU 上同步訓練模型。with strategy.scope()::確保模型和相關的操作在分散式策略的範圍內建立,從而實作分散式訓練。