# 使用 EfficientNet 預測髖關節 X 光影像關鍵點
## 專案目標
- 預測 X 光影像中的 8 個髖關節關鍵點,輸出對應的 16 個座標(x, y)。
- 使用 EfficientNetB0 作為基底模型進行訓練和微調,結合資料增強技術提高模型泛化能力。
---
## 專案進展
### 1. 資料預處理
**程式碼:**
```python
def load_labels(image_name):
label_path = os.path.join(labels_folder_path, image_name.replace('.jpg', '.csv'))
labels_df = pd.read_csv(label_path, header=None)
labels = labels_df.values.flatten()
parsed_labels = []
for label in labels:
label = label.strip('()')
x, y = map(float, label.split(','))
parsed_labels.extend([x, y])
return np.array(parsed_labels)
def load_image(image_name, target_size=(224, 224)):
image_path = os.path.join(images_path, image_name)
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
original_size = (image.shape[1], image.shape[0]) # (width, height)
image = cv2.resize(image, target_size)
image = image.astype(np.float32) / 255.0
image = np.stack([image] * 3, axis=-1) # 擴充成3通道
return image, original_size
def preprocess_labels(labels, original_size, target_size=(224, 224)):
x_ratio = target_size[0] / original_size[0]
y_ratio = target_size[1] / original_size[1]
return labels.astype(float) * np.array([x_ratio, y_ratio] * (len(labels) // 2))
```
- **影像處理:**
將灰階影像轉換為 224x224 的 3 通道影像,並進行標準化。
- **標籤處理:**
將每張影像對應的標籤 `(x, y)` 座標縮放到 224x224 的範圍。
---
### 2. 資料增強
**程式碼:**
```python
seq = iaa.Sequential([
iaa.Fliplr(0.5), # 水平翻轉
iaa.Affine(
rotate=(-5, 5), # 減小旋轉角度
translate_percent={"x": (-0.01, 0.01), "y": (-0.01, 0.01)}, # 減小平移幅度
scale=(0.98, 1.02) # 減小縮放幅度
),
iaa.Multiply((0.95, 1.05)) # 輕微亮度變化
], random_order=True)
```
- 使用 `imgaug` 定義增強策略,包括翻轉、旋轉、平移、縮放與亮度調整。
- 確保影像與關鍵點同步進行增強。
---
### 3. 模型設計
**程式碼:**
```python
import efficientnet.tfkeras as efn
base_model = efn.EfficientNetB0(input_shape=(resolution, resolution, 3), include_top=False, weights='imagenet')
base_model.trainable = True
for layer in base_model.layers[:-24]:
layer.trainable = False
global_avg_pool = tf.keras.layers.GlobalAveragePooling2D()(base_model.output)
dense_1 = tf.keras.layers.Dense(256, activation='relu', kernel_regularizer=tf.keras.regularizers.l2(0.02))(global_avg_pool)
dense_2 = tf.keras.layers.Dense(128, activation='relu')(dense_1)
dropout = tf.keras.layers.Dropout(0.5)(dense_2)
output_layer = tf.keras.layers.Dense(16, activation='linear')(dropout)
model = tf.keras.models.Model(inputs=base_model.input, outputs=output_layer)
```
- 使用 EfficientNetB0 作為基底模型,添加自定義全連接層進行關鍵點回歸。
- 凍結前 24 層參數以利用預訓練權重。
---
### 4. 損失函數設計
**程式碼:**
```python
def cosine_similarity_loss(y_true, y_pred):
def get_vectors(keypoints):
keypoints = tf.reshape(keypoints, [-1, 2])
vectors = keypoints[:-1] - keypoints[1:]
return vectors
y_true_vectors = get_vectors(y_true)
y_pred_vectors = get_vectors(y_pred)
dot_product = tf.reduce_sum(y_true_vectors * y_pred_vectors, axis=-1)
norm_true = tf.norm(y_true_vectors, axis=-1)
norm_pred = tf.norm(y_pred_vectors, axis=-1)
cosine_similarity = dot_product / (norm_true * norm_pred + 1e-8)
return tf.reduce_mean(1 - cosine_similarity)
def combined_loss(y_true, y_pred):
mse_loss_value = tf.keras.losses.MeanSquaredError()(y_true, y_pred)
cosine_loss_value = cosine_similarity_loss(y_true, y_pred)
return mse_loss_value + 0.5 * cosine_loss_value
```
- 自定義損失函數包括:
- MSE(均方誤差)。
- Cosine Similarity(確保相對位置一致性)。
---
### 5. 模型訓練
**程式碼:**
```python
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4),
loss=combined_loss,
metrics=['mae', 'mse'])
history_stage1 = model.fit(train_generator,
steps_per_epoch=steps_per_epoch,
validation_data=val_generator,
validation_steps=validation_steps,
epochs=30)
```
- **訓練策略:**
- 初始訓練凍結大部分層,學習率為 `1e-4`。
- 中期與後期微調時逐步解凍 EfficientNet 層。
---
### 6. 測試集評估與可視化
**程式碼:**
```python
test_loss, test_mae = model.evaluate(test_generator, steps=test_steps)
print(f"Test Loss: {test_loss}")
print(f"Test MAE: {test_mae}")
for i, (test_images, test_labels) in enumerate(test_generator):
if i >= display_count:
break
predictions = model.predict(test_images)
for j in range(len(test_images)):
plt.imshow(test_images[j])
plt.scatter(predictions[j][::2], predictions[j][1::2], color='r', label='Predicted')
plt.scatter(test_labels[j][::2], test_labels[j][1::2], color='g', label='True')
plt.legend()
plt.show()
```
- 評估測試集的損失與 MAE。
- 可視化模型預測結果,檢查關鍵點定位效果。
---
### 7. 訓練曲線
**程式碼:**
```python
all_train_loss = history_stage1.history['loss'] + history_stage2.history['loss'] + history_stage3.history['loss']
all_val_loss = history_stage1.history['val_loss'] + history_stage2.history['val_loss'] + history_stage3.history['val_loss']
plt.plot(all_train_loss, label='Training Loss')
plt.plot(all_val_loss, label='Validation Loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show()
```
- 繪製訓練與驗證損失曲線,觀察模型收斂效果。
---
### 目前問題
