返回文章列表

PoseNet 姿勢估計技術詳解與應用

PoseNet 是一種根據深度學習的姿勢估計技術,能夠識別圖片或影片中的人體關節位置。本文探討 PoseNet 的原理、實作方法,包含單人與多人姿勢估計的程式碼範例,並解析輸出結果的解讀方式。此外,文章也探討了影像搜尋技術,包含分散式搜尋和使用 ScaNN 等高效搜尋演算法,以及如何利用 MobileNet 和

機器學習 電腦視覺

PoseNet 利用卷積神經網路,特別是 MobileNet 或 ResNet 等預訓練模型,來進行人體姿勢估計。透過微調這些模型,使其適應姿勢估計任務,能有效偵測人體關鍵點。實作上,需載入 PoseNet 函式庫並初始化模型,再利用 estimateSinglePoseestimateMultiplePose 函式對單人或多人影像進行姿態估計。結果輸出為 JSON 格式,包含各關節位置與置信度分數。多人姿態估計則需結合影像分割模型,區分不同人物並分配關節資訊。文章也提及影像搜尋技術,可利用分散式資料倉儲如 BigQuery 進行大規模搜尋,或使用 ScaNN 等高效演算法提升即時搜尋效能。為最佳化搜尋效果,可運用 MobileNet 建立影像嵌入,並結合 Triplet Loss 訓練模型,提升嵌入表示的準確性。最後,文章也介紹了影像理解與生成技術,包含遷移學習、輔助學習任務以及自動編碼器等方法,並提供程式碼範例說明如何應用這些技術於影像處理任務。

PoseNet姿勢估計技術詳解與應使用案例項

PoseNet是一種根據深度學習的姿勢估計技術,能夠在圖片或影片中準確地識別人體關節位置。本文將探討PoseNet的工作原理、實作方法以及相關應用。

PoseNet基本原理

PoseNet利用卷積神經網路(CNN)來估計人體姿勢。該技術根據預訓練的分類別模型(如MobileNet或ResNet),並透過微調來適應姿勢估計任務。

實作PoseNet姿勢估計

要使用PoseNet,首先需要載入相關函式庫並初始化模型。以下是一個簡單的範例:

posenet.load().then(function(net) {
  const pose = net.estimateSinglePose(imageElement, {
    flipHorizontal: false
  });
  return pose;
})

內容解密:

  1. posenet.load():載入PoseNet模型。
  2. net.estimateSinglePose(imageElement, {...}):對指定的圖片元素進行單一人體姿勢估計。
  3. flipHorizontal: false:控制是否水平翻轉圖片,對於自拍圖片可能需要設定為true

輸出結果與解讀

PoseNet輸出的結果是一個JSON物件,包含了人體各個關節的位置資訊。例如:

{
  "score": 0.5220872163772583,
  "part": "leftEar",
  "position": {
    "x": 342.9179292671411,
    "y": 91.27406275411522
  }
}

內容解密:

  1. score:表示該關節的信心度得分。
  2. part:表示人體的某個部位(例如左耳)。
  3. position:包含該部位的x和y座標。

多人姿勢估計

對於包含多人的圖片,PoseNet同樣能夠進行姿勢估計。需要額外的步驟來區分不同的人物:

  1. 使用影像分割模型識別人物區域。
  2. 結合關節資訊,識別特定身體部位的位置。
  3. 將分割遮罩和關節連線資訊結合,分配人物畫素給不同的人。
net.estimateMultiplePoses(image, {
  flipHorizontal: false,
  maxDetections: 5,
  scoreThreshold: 0.5,
  nmsRadius: 20
});

內容解密:

  1. maxDetections:控制最多偵測的人數。
  2. scoreThreshold:設定信心度閾值。
  3. nmsRadius:控制非極大值抑制的半徑。

影像搜尋技術

影像搜尋是電腦視覺中的另一個重要應用。透過將影像轉換為嵌入向量(embeddings),可以實作高效的相似影像搜尋。

分散式搜尋

使用大規模分散式資料倉儲(如Google BigQuery),可以實作對大量影像嵌入向量的高效搜尋。範例如下:

WITH ref1 AS (
  SELECT time AS ref1_time, ref1_value, ref1_offset
  FROM `ai-analytics-solutions.advdata.wxembed`,
  UNNEST(ref) AS ref1_value WITH OFFSET AS ref1_offset
  WHERE time = '2019-09-20 05:00:00 UTC'
)
SELECT 
  time,
  SUM( (ref1_value - ref[OFFSET(ref1_offset)]) 
       * (ref1_value - ref[OFFSET(ref1_offset)]) ) AS sqdist
FROM ref1, `ai-analytics-solutions.advdata.wxembed`
GROUP BY 1
ORDER By sqdist ASC
LIMIT 5

內容解密:

  1. 使用CTE(Common Table Expression)ref1儲存參考時間的嵌入向量。
  2. 計算參考向量與資料函式庫中其他向量之間的歐幾裡得距離。
  3. 按距離排序並傳回最相似的結果。

高效搜尋技術

對於即時服務需求,可以使用Scalable Nearest Neighbors(ScaNN)等高效近鄰搜尋演算法來加速搜尋過程。

影像搜尋的最佳化技術

在影像搜尋領域中,建立高效的嵌入式表示(embeddings)是至關重要的。嵌入式表示能夠將影像對映到一個向量空間中,使得相似的影像在該空間中距離較近,而不同的影像距離較遠。本篇文章將探討如何使用 MobileNet 和 triplet loss 技術來最佳化影像嵌入式表示,並使用 ScaNN 進行高效的影像搜尋。

使用 MobileNet 建立初始嵌入式表示

首先,我們使用預訓練的 MobileNet 模型來建立影像的嵌入式表示。MobileNet 是一種輕量級的卷積神經網路,適合於移動裝置上的影像分類別任務。我們利用其預訓練的權重來提取影像特徵。

layers = [
    hub.KerasLayer(
        "https://tfhub.dev/.../mobilenet_v2/feature_vector/4",
        input_shape=(IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS),
        trainable=False,
        name='mobilenet_embedding'),
    tf.keras.layers.Flatten()
]
model = tf.keras.Sequential(layers, name='flowers_embedding')

內容解密:

  1. hub.KerasLayer:使用 TensorFlow Hub 中的預訓練 MobileNet 模型來提取影像特徵。
  2. input_shape:指定輸入影像的尺寸和通道數。
  3. trainable=False:凍結 MobileNet 的權重,不參與後續的訓練。
  4. tf.keras.layers.Flatten():將輸出展平,以便於後續處理。

建立嵌入式表示資料集

接下來,我們遍歷影像資料集,使用建立的模型來預測影像的嵌入式表示,並將結果儲存起來。

def create_embeddings_dataset(csvfilename):
    ds = (tf.data.TextLineDataset(csvfilename).
          map(decode_csv).batch(BATCH_SIZE))
    dataset_filenames = []
    dataset_embeddings = []
    for filenames, images in ds:
        embeddings = model.predict(images)
        dataset_filenames.extend(
            [f.numpy().decode('utf-8') for f in filenames])
        dataset_embeddings.extend(embeddings)
    dataset_embeddings = tf.convert_to_tensor(dataset_embeddings)
    return dataset_filenames, dataset_embeddings

內容解密:

  1. tf.data.TextLineDataset:讀取 CSV 檔案中的影像檔名。
  2. map(decode_csv):將 CSV 檔案中的資料解碼為影像。
  3. batch(BATCH_SIZE):將影像分批處理。
  4. model.predict(images):使用模型預測影像的嵌入式表示。
  5. dataset_embeddings.extend(embeddings):將預測結果儲存起來。

使用 ScaNN 進行高效的影像搜尋

有了嵌入式表示資料集後,我們可以使用 ScaNN 來建立高效的影像搜尋索引。

searcher = scann.scann_ops.builder(
    dataset_embeddings,
    NUM_NEIGH, "dot_product").score_ah(2).build()

內容解密:

  1. scann.scann_ops.builder:建立 ScaNN 搜尋索引。
  2. dataset_embeddings:輸入嵌入式表示資料集。
  3. NUM_NEIGH:指定要搜尋的鄰居數量。
  4. “dot_product”:使用點積作為相似度度量。
  5. score_ah(2):設定量化引數。

使用 Triplet Loss 最佳化嵌入式表示

為了進一步提高影像搜尋的準確度,我們可以使用 triplet loss 技術來最佳化嵌入式表示。

layers = [
    hub.KerasLayer(
        "https://tfhub.dev/.../mobilenet_v2/feature_vector/4",
        input_shape=(IMG_HEIGHT, IMG_WIDTH, IMG_CHANNELS),
        trainable=False,
        name='mobilenet_embedding'),
    tf.keras.layers.Dense(5, activation=None, name='dense_5'),
    tf.keras.layers.Lambda(lambda x: tf.math.l2_normalize(x, axis=1),
                           name='normalize_embeddings')
]
model = tf.keras.Sequential(layers, name='flowers_embedding')
model.compile(optimizer=tf.keras.optimizers.Adam(0.001),
              loss=tfa.losses.TripletSemiHardLoss())

內容解密:

  1. tf.keras.layers.Dense(5):新增一個線性層,將嵌入式表示的維度降至 5。
  2. tf.keras.layers.Lambda:對輸出進行 L2 正則化。
  3. tfa.losses.TripletSemiHardLoss():使用 triplet loss 作為損失函式。
  4. trainable=False:凍結 MobileNet 的權重。

影像與文字生成技術

在前面的章節中,我們探討了根據基本電腦視覺技術的多種應用案例。本章將重點放在能夠生成影像的視覺方法上。在深入影像生成之前,我們需要了解如何訓練模型來理解影像內容,以便知道該生成什麼。此外,我們還會探討根據影像內容生成文字(標題)的問題。

本章的程式碼位於本文 GitHub 儲存函式庫的 12_generation 資料夾中。我們將在適當的地方提供程式碼範例和筆記本的檔案名稱。

影像理解

瞭解影像中的組成部分是一回事,但真正理解影像中正在發生的事情並利用這些資訊進行其他任務則是另一回事。在本文中,我們將快速回顧嵌入(embeddings),然後探討各種方法(自動編碼器和變分自動編碼器)來編碼影像並瞭解其屬性。

嵌入(Embeddings)

深度學習應用案例中的一個常見問題是缺乏足夠的資料或足夠高品質的資料。在第3章中,我們討論了遷移學習(transfer learning),它提供了一種方法,可以提取從在較大資料集上訓練的模型中學習到的嵌入,並將這些知識應用於在較小資料集上訓練有效的模型。

# 使用 Keras 實作遷移學習的範例
from tensorflow.keras.applications import ResNet50
from tensorflow.keras.models import Model

# 載入預訓練的 ResNet50 模型
base_model = ResNet50(weights='imagenet', include_top=False, input_shape=(224, 224, 3))

# 凍結基底模型的權重
base_model.trainable = False

# 新增自定義層
x = base_model.output
x = GlobalAveragePooling2D()(x)
x = Dense(1024, activation='relu')(x)
predictions = Dense(10, activation='softmax')(x)

# 建立新模型
model = Model(inputs=base_model.input, outputs=predictions)

內容解密:

  1. 載入預訓練的 ResNet50 模型,並設定 include_top=False 以排除頂層分類別層,保留卷積基底。
  2. 將基底模型的權重設為不可訓練,以保留預訓練的特徵提取能力。
  3. 新增自定義層,包括全域性平均池化層、Dense 層和最終的分類別層,以適應新的任務。
  4. 建立新的模型,將預訓練基底和自定義層結合,實作遷移學習。

遷移學習的一個問題是,所建立的嵌入是否能夠很好地泛化到與訓練資料不同的影像型別。為瞭解決這個問題,我們可以使用輔助學習任務(auxiliary learning task)來建立嵌入。

輔助學習任務

輔助學習任務是一種與我們試圖解決的實際監督學習問題不同的任務。這種任務應該是具有大量可用資料的任務。例如,在文字分類別的情況下,我們可以使用預測句子下一個詞的任務來建立文字嵌入,因為這個任務有大量的可用訓練資料。

# 使用 Keras 實作輔助學習任務的範例(以預測下一個詞為例)
from tensorflow.keras.preprocessing.sequence import pad_sequences
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Embedding, LSTM, Dense

# 定義模型架構
model = Sequential()
model.add(Embedding(input_dim=10000, output_dim=128, input_length=max_length))
model.add(LSTM(128))
model.add(Dense(10000, activation='softmax'))

# 編譯模型
model.compile(loss='sparse_categorical_crossentropy', optimizer='adam', metrics=['accuracy'])

內容解密:

  1. 使用 Embedding 層將輸入的詞索引轉換為密集向量表示。
  2. 使用 LSTM 層處理序列資料,捕捉上下文資訊。
  3. 最終使用 Dense 層進行預測,輸出下一個詞的機率分佈。
  4. 編譯模型時,使用 sparse_categorical_crossentropy 作為損失函式,因為目標是詞索引。

自動編碼器(Autoencoders)

自動編碼器是一種優秀的輔助學習任務,用於學習影像嵌入。自動編碼器將影像資料透過一個網路,該網路將其壓縮到一個較小的內部向量,然後將其擴充套件回原始影像。

# 使用 Keras 實作自動編碼器的範例
from tensorflow.keras.layers import Input, Dense
from tensorflow.keras.models import Model

# 定義輸入層
input_img = Input(shape=(784,))

# 編碼器
encoded = Dense(128, activation='relu')(input_img)
encoded = Dense(64, activation='relu')(encoded)
encoded = Dense(32, activation='relu')(encoded)

# 解碼器
decoded = Dense(64, activation='relu')(encoded)
decoded = Dense(128, activation='relu')(decoded)
decoded = Dense(784, activation='sigmoid')(decoded)

# 自動編碼器模型
autoencoder = Model(input_img, decoded)

# 編譯模型
autoencoder.compile(optimizer='adam', loss='binary_crossentropy')

內容解密:

  1. 定義輸入層,假設輸入是784維的向量(例如,28x28的灰階影像)。
  2. 編碼器部分逐漸降低維度,將輸入壓縮到32維。
  3. 解碼器部分逐漸增加維度,將壓縮表示還原回原始維度。
  4. 編譯自動編碼器模型,使用二元交叉熵作為損失函式,因為輸出是畫素值(0到1之間)。