權重分群綜合指南

在 TensorFlow.org 上檢視 在 Google Colab 中執行 在 GitHub 上檢視原始碼 下載筆記本

歡迎閱讀權重分群綜合指南,這是 TensorFlow 模型最佳化工具組的一部分。

本頁面記錄各種使用案例,並說明如何針對每個案例使用 API。一旦您知道所需的 API,請在API 文件中尋找參數和低階詳細資料

  • 如果您想瞭解權重分群的優點和支援的功能,請查看總覽
  • 如需單一端對端範例,請參閱權重分群範例

本指南涵蓋以下使用案例

  • 定義分群模型。
  • 檢查點和還原序列化分群模型。
  • 提升分群模型的準確性。
  • 僅針對部署,您必須採取步驟才能看到壓縮優勢。

設定

! pip install -q tensorflow-model-optimization

import tensorflow as tf
import tf_keras as keras
import numpy as np
import tempfile
import os
import tensorflow_model_optimization as tfmot

input_dim = 20
output_dim = 20
x_train = np.random.randn(1, input_dim).astype(np.float32)
y_train = keras.utils.to_categorical(np.random.randn(1), num_classes=output_dim)

def setup_model():
  model = keras.Sequential([
      keras.layers.Dense(input_dim, input_shape=[input_dim]),
      keras.layers.Flatten()
  ])
  return model

def train_model(model):
  model.compile(
      loss=keras.losses.categorical_crossentropy,
      optimizer='adam',
      metrics=['accuracy']
  )
  model.summary()
  model.fit(x_train, y_train)
  return model

def save_model_weights(model):
  _, pretrained_weights = tempfile.mkstemp('.h5')
  model.save_weights(pretrained_weights)
  return pretrained_weights

def setup_pretrained_weights():
  model= setup_model()
  model = train_model(model)
  pretrained_weights = save_model_weights(model)
  return pretrained_weights

def setup_pretrained_model():
  model = setup_model()
  pretrained_weights = setup_pretrained_weights()
  model.load_weights(pretrained_weights)
  return model

def save_model_file(model):
  _, keras_file = tempfile.mkstemp('.h5') 
  model.save(keras_file, include_optimizer=False)
  return keras_file

def get_gzipped_model_size(model):
  # It returns the size of the gzipped model in bytes.
  import os
  import zipfile

  keras_file = save_model_file(model)

  _, zipped_file = tempfile.mkstemp('.zip')
  with zipfile.ZipFile(zipped_file, 'w', compression=zipfile.ZIP_DEFLATED) as f:
    f.write(keras_file)
  return os.path.getsize(zipped_file)

setup_model()
pretrained_weights = setup_pretrained_weights()
2024-03-09 12:38:26.610043: E external/local_xla/xla/stream_executor/cuda/cuda_driver.cc:282] failed call to cuInit: CUDA_ERROR_NO_DEVICE: no CUDA-capable device is detected

定義分群模型

叢集整個模型 (循序和函數式)

提升模型準確性的訣竅

  • 您必須將具有可接受準確性的預先訓練模型傳遞至此 API。從頭開始訓練具有分群的模型會導致準確性不佳。
  • 在某些情況下,叢集某些層級會對模型準確性產生不利影響。請查看「叢集某些層級」,以瞭解如何略過叢集對準確性影響最大的層級。

若要叢集所有層級,請將 tfmot.clustering.keras.cluster_weights 套用至模型。

import tensorflow_model_optimization as tfmot

cluster_weights = tfmot.clustering.keras.cluster_weights
CentroidInitialization = tfmot.clustering.keras.CentroidInitialization

clustering_params = {
  'number_of_clusters': 3,
  'cluster_centroids_init': CentroidInitialization.KMEANS_PLUS_PLUS
}

model = setup_model()
model.load_weights(pretrained_weights)

clustered_model = cluster_weights(model, **clustering_params)

clustered_model.summary()
Model: "sequential_2"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 cluster_dense_2 (ClusterWe  (None, 20)                823       
 ights)                                                          
                                                                 
 cluster_flatten_2 (Cluster  (None, 20)                0         
 Weights)                                                        
                                                                 
=================================================================
Total params: 823 (4.78 KB)
Trainable params: 423 (1.65 KB)
Non-trainable params: 400 (3.12 KB)
_________________________________________________________________

叢集某些層級 (循序和函數式模型)

提升模型準確性的訣竅

  • 您必須將具有可接受準確性的預先訓練模型傳遞至此 API。從頭開始訓練具有分群的模型會導致準確性不佳。
  • 叢集具有更多冗餘參數的後續層級 (例如 keras.layers.Densekeras.layers.Conv2D),而不是早期層級。
  • 在微調期間,於叢集層級之前凍結早期層級。將凍結層級的數量視為超參數。根據經驗,凍結大多數早期層級對於目前的叢集 API 而言是理想的。
  • 避免叢集關鍵層級 (例如注意力機制)。

更多資訊tfmot.clustering.keras.cluster_weights API 文件提供有關如何針對每個層級變更叢集組態的詳細資訊。

# Create a base model
base_model = setup_model()
base_model.load_weights(pretrained_weights)

# Helper function uses `cluster_weights` to make only 
# the Dense layers train with clustering
def apply_clustering_to_dense(layer):
  if isinstance(layer, keras.layers.Dense):
    return cluster_weights(layer, **clustering_params)
  return layer

# Use `keras.models.clone_model` to apply `apply_clustering_to_dense` 
# to the layers of the model.
clustered_model = keras.models.clone_model(
    base_model,
    clone_function=apply_clustering_to_dense,
)

clustered_model.summary()
Model: "sequential_3"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 cluster_dense_3 (ClusterWe  (None, 20)                823       
 ights)                                                          
                                                                 
 flatten_3 (Flatten)         (None, 20)                0         
                                                                 
=================================================================
Total params: 823 (4.78 KB)
Trainable params: 423 (1.65 KB)
Non-trainable params: 400 (3.12 KB)
_________________________________________________________________

依通道叢集卷積層

叢集模型可以傳遞至進一步最佳化,例如訓練後量化。如果量化是依通道完成的,則模型也應依通道叢集。這會提高叢集和量化模型的準確性。

若要依通道叢集,參數 cluster_per_channel 應設為 True。它可以針對某些層級或整個模型設定。

訣竅

  • 如果模型要進一步量化,您可以考慮使用叢集保留 QAT 技術

  • 模型可以在套用依通道叢集之前進行剪枝。如果參數 preserve_sparsity 設定為 True,則會在依通道叢集期間保留稀疏性。請注意,在這種情況下應使用稀疏性和叢集保留 QAT 技術

叢集自訂 Keras 層級或指定要叢集的層級權重

tfmot.clustering.keras.ClusterableLayer 適用於兩種使用案例

  1. 叢集任何原生不支援的層級,包括自訂 Keras 層級。
  2. 指定要叢集之支援層級的權重。

例如,API 預設為僅叢集 Dense 層級的核心。以下範例示範如何修改它以叢集偏差。請注意,從 keras 層級衍生時,您需要覆寫函式 get_clusterable_weights,您可以在其中指定要叢集的可訓練變數名稱和可訓練變數本身。例如,如果您傳回空清單 [],則不會有任何權重可叢集。

常見錯誤:叢集偏差通常會嚴重損害模型準確性。

class MyDenseLayer(keras.layers.Dense, tfmot.clustering.keras.ClusterableLayer):

  def get_clusterable_weights(self):
   # Cluster kernel and bias. This is just an example, clustering
   # bias usually hurts model accuracy.
   return [('kernel', self.kernel), ('bias', self.bias)]

# Use `cluster_weights` to make the `MyDenseLayer` layer train with clustering as usual.
model_for_clustering = keras.Sequential([
  tfmot.clustering.keras.cluster_weights(MyDenseLayer(20, input_shape=[input_dim]), **clustering_params),
  keras.layers.Flatten()
])

model_for_clustering.summary()
Model: "sequential_4"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 cluster_my_dense_layer (Cl  (None, 20)                846       
 usterWeights)                                                   
                                                                 
 flatten_4 (Flatten)         (None, 20)                0         
                                                                 
=================================================================
Total params: 846 (4.95 KB)
Trainable params: 426 (1.66 KB)
Non-trainable params: 420 (3.28 KB)
_________________________________________________________________

您也可以使用 tfmot.clustering.keras.ClusterableLayer 來叢集 keras 自訂層級。若要執行此動作,請照常擴充 keras.Layer 並實作 __init__callbuild 函式,但您也需要擴充 clusterable_layer.ClusterableLayer 類別並實作

  1. get_clusterable_weights,您可以在其中指定要叢集的權重核心,如上所示。
  2. get_clusterable_algorithm,您可以在其中指定權重張量的叢集演算法。這是因為您需要指定自訂層級權重的形狀以進行叢集。傳回的叢集演算法類別應衍生自 clustering_algorithm.ClusteringAlgorithm 類別,且應覆寫函式 get_pulling_indices。此函式的範例 (支援等級為 1D、2D 和 3D 的權重) 可在此處找到

此使用案例的範例可在此處找到

檢查點和還原序列化分群模型

您的使用案例:此程式碼僅適用於 HDF5 模型格式 (而非 HDF5 權重或其他格式)。

# Define the model.
base_model = setup_model()
base_model.load_weights(pretrained_weights)
clustered_model = cluster_weights(base_model, **clustering_params)

# Save or checkpoint the model.
_, keras_model_file = tempfile.mkstemp('.h5')
clustered_model.save(keras_model_file, include_optimizer=True)

# `cluster_scope` is needed for deserializing HDF5 models.
with tfmot.clustering.keras.cluster_scope():
  loaded_model = keras.models.load_model(keras_model_file)

loaded_model.summary()
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
WARNING:tensorflow:No training configuration found in the save file, so the model was *not* compiled. Compile it manually.
Model: "sequential_5"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 cluster_dense_4 (ClusterWe  (None, 20)                823       
 ights)                                                          
                                                                 
 cluster_flatten_5 (Cluster  (None, 20)                0         
 Weights)                                                        
                                                                 
=================================================================
Total params: 823 (4.78 KB)
Trainable params: 423 (1.65 KB)
Non-trainable params: 400 (3.12 KB)
_________________________________________________________________
/tmpfs/src/tf_docs_env/lib/python3.9/site-packages/tf_keras/src/engine/training.py:3098: UserWarning: You are saving your model as an HDF5 file via `model.save()`. This file format is considered legacy. We recommend using instead the native TF-Keras format, e.g. `model.save('my_model.keras')`.
  saving_api.save_model(

提升分群模型的準確性

針對您的特定使用案例,您可以考慮以下訣竅

  • 質心初始化在最終最佳化模型準確性中扮演關鍵角色。一般而言,kmeans++ 初始化優於線性、密度和隨機初始化。當不使用 kmeans++ 時,線性初始化往往優於密度和隨機初始化,因為它不會傾向於遺漏大型權重。但是,已觀察到密度初始化在權重上使用極少叢集且具有雙峰分佈的情況下,可提供更好的準確性。

  • 設定低於微調分群模型時所用學習率的學習率。

  • 如需改善模型準確性的一般概念,請在「定義分群模型」下尋找適用於您的使用案例的訣竅。

部署

匯出具有大小壓縮的模型

常見錯誤:必須同時執行 strip_clustering 和套用標準壓縮演算法 (例如透過 gzip),才能看到分群的壓縮優勢。

model = setup_model()
clustered_model = cluster_weights(model, **clustering_params)

clustered_model.compile(
    loss=keras.losses.categorical_crossentropy,
    optimizer='adam',
    metrics=['accuracy']
)

clustered_model.fit(
    x_train,
    y_train
)

final_model = tfmot.clustering.keras.strip_clustering(clustered_model)

print("final model")
final_model.summary()

print("\n")
print("Size of gzipped clustered model without stripping: %.2f bytes" 
      % (get_gzipped_model_size(clustered_model)))
print("Size of gzipped clustered model with stripping: %.2f bytes" 
      % (get_gzipped_model_size(final_model)))
1/1 [==============================] - 1s 990ms/step - loss: 16.1181 - accuracy: 0.0000e+00
final model
Model: "sequential_6"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 dense_5 (Dense)             (None, 20)                420       
                                                                 
 flatten_6 (Flatten)         (None, 20)                0         
                                                                 
=================================================================
Total params: 420 (1.64 KB)
Trainable params: 420 (1.64 KB)
Non-trainable params: 0 (0.00 Byte)
_________________________________________________________________


Size of gzipped clustered model without stripping: 3516.00 bytes
WARNING:tensorflow:Compiled the loaded model, but the compiled metrics have yet to be built. `model.compile_metrics` will be empty until you train or evaluate the model.
Size of gzipped clustered model with stripping: 1469.00 bytes