# What Is the Auxiliary Network in PFLD? Architecture, Role, and Implementation

> Discover the auxiliary network in PFLD it predicts head-pose Euler angles to regularize landmark regression via pose-aware loss weighting Learn its architecture and role

- Repository: [Guoqiang QI/pfld](https://github.com/guoqiangqi/pfld)
- Tags: deep-dive
- Published: 2026-03-02

---

**The auxiliary network in PFLD predicts head-pose Euler angles to regularize landmark regression through a pose-aware loss weighting mechanism.**

The PFLD (Progressive Face Localization-Detection) repository by `guoqiangqi/pfld` implements a lightweight facial landmark detector that uses multi-task learning to improve accuracy across varying head poses. The auxiliary network serves as a secondary prediction head that estimates three-dimensional head orientation, enabling the model to apply dynamic loss weighting that penalizes misalignment more heavily when the face is turned away from the camera.

## Architecture of the Auxiliary Network in PFLD

The auxiliary network branches off from the main backbone at an intermediate feature layer, processing the shared representations through a shallow convolutional head to produce three scalar outputs representing pitch, yaw, and roll.

### Feature Extraction Point (model2.py)

In [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py), the backbone constructs a dictionary `features` to store intermediate tensors for branching. After the third residual block (`block3_5`), the feature map is captured at line 887:

```python
features['auxiliary_input'] = block3_5

```

This tensor, typically shaped `[batch, height, width, channels]`, serves as the input to the auxiliary head while maintaining gradient flow back to the shared backbone.

### Network Layers and Output

The auxiliary head is constructed within the `create_model` function using TensorFlow-Slim operations. The architecture consists of:

1. **Convolutional reduction**: A 3×3 convolution with 128 filters and stride 2 reduces spatial dimensions while increasing depth.
2. **Flattening**: The spatial tensor is flattened to a vector.
3. **Fully-connected layers**: Two dense layers project the features to 32 dimensions and finally to 3 outputs.

The implementation from [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py) (lines 887-936) follows this pattern:

```python
pfld_input = features['auxiliary_input']
net_aux = slim.convolution2d(pfld_input, 128, [3, 3], stride=2, scope='pfld_conv1')

# ... additional convolutions ...

net_aux = slim.flatten(net_aux)
fc1 = slim.fully_connected(net_aux, num_outputs=32, activation_fn=None, scope='pfld_fc1')
euler_angles_pre = slim.fully_connected(fc1, num_outputs=3, activation_fn=None, scope='pfld_fc2')

```

The final tensor `euler_angles_pre` contains the predicted **pitch, yaw, and roll** angles, which are returned alongside the landmark predictions for use in the loss computation and downstream applications.

## How the Auxiliary Network Regulates Training

The auxiliary network influences the optimization process through a pose-aware loss weighting mechanism that dynamically adjusts the penalty for landmark errors based on the estimated head orientation.

### Pose-Aware Loss Weighting (train_model.py)

In [`train_model.py`](https://github.com/guoqiangqi/pfld/blob/main/train_model.py), the ground-truth Euler angles (`euler_angles_gt_batch`) are compared against the auxiliary network's predictions (`euler_angles_pre`). The discrepancy is converted into a weighting factor `_sum_k` using a cosine penalty:

```python
_sum_k = tf.reduce_sum(
            tf.map_fn(lambda x: 1 - tf.cos(tf.abs(x)),
                      euler_angles_gt_batch - euler_angles_pre), axis=1)
loss_sum = tf.reduce_mean(loss_sum * _sum_k * attributes_w_n)

```

This formulation (lines 98-100) ensures that when the auxiliary network predicts a head pose that diverges from the actual orientation, the landmark loss is amplified. Conversely, when the pose estimate is accurate, the loss weight remains moderate. This forces the backbone to learn pose-invariant features that maintain landmark consistency across extreme angles.

### Multi-Task Learning Benefits

By jointly optimizing for both landmark regression and pose estimation, the auxiliary network provides **additional supervision signals** to the shared convolutional layers. This multi-task framework:

- Prevents overfitting to landmark coordinates alone by requiring the network to also decode geometric orientation.
- Encourages the backbone to preserve spatial information that is critical for both tasks, resulting in richer feature representations.
- Enables the model to generalize better to unseen poses, as the auxiliary loss acts as a regularizer during training.

## Implementation Details and Code Example

The following example demonstrates how to instantiate the PFLD model and extract both landmark predictions and the auxiliary network's pose estimates using the TensorFlow graph defined in [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py).

```python
import tensorflow as tf
from model2 import create_model

# Define input placeholders matching the training configuration

image_ph = tf.placeholder(tf.float32, [None, 112, 112, 3], name='image')
landmark_ph = tf.placeholder(tf.float32, [None, 196], name='landmarks')
phase_ph = tf.placeholder(tf.bool, name='phase_train')

# Minimal configuration object required by the model builder

class Config:
    weight_decay = 5e-4
    image_size = 112

args = Config()

# Build the network graph

landmarks_pred, landmarks_loss, euler_angles_pred = create_model(
    image_ph, landmark_ph, phase_ph, args
)

# `euler_angles_pred` contains the auxiliary network output [batch, 3]

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    
    # Example inference with dummy data

    dummy_images = np.random.randn(4, 112, 112, 3).astype(np.float32)
    dummy_landmarks = np.zeros((4, 196), dtype=np.float32)
    
    landmarks_out, pose_out = sess.run(
        [landmarks_pred, euler_angles_pred],
        feed_dict={
            image_ph: dummy_images,
            landmark_ph: dummy_landmarks,
            phase_ph: False
        }
    )
    
    print(f"Landmark predictions shape: {landmarks_out.shape}")
    print(f"Auxiliary pose prediction (pitch, yaw, roll): {pose_out[0]}")

```

## Summary

- The **auxiliary network** in PFLD is a secondary prediction head that branches from the `block3_5` features in [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py) to estimate head-pose Euler angles.
- It consists of a lightweight stack: a 3×3 convolution with stride 2, flattening, and two fully-connected layers outputting three values (pitch, yaw, roll).
- During training, the discrepancy between predicted and ground-truth angles generates a **pose-aware weight** (`_sum_k`) in [`train_model.py`](https://github.com/guoqiangqi/pfld/blob/main/train_model.py) that scales the landmark loss, penalizing errors more heavily for extreme poses.
- This multi-task architecture improves landmark accuracy across pose variations while providing a useful side-output for applications requiring head-orientation estimation.

## Frequently Asked Questions

### What does the auxiliary network output in PFLD?

The auxiliary network outputs a 3-dimensional vector representing the **Euler angles** (pitch, yaw, and roll) of the head pose. In [`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py), the final fully-connected layer `pfld_fc2` produces this tensor named `euler_angles_pre`, which is returned alongside the landmark predictions for use in the loss calculation and inference.

### How does the auxiliary network improve landmark accuracy?

The auxiliary network improves accuracy by acting as a **pose-aware regularizer**. In [`train_model.py`](https://github.com/guoqiangqi/pfld/blob/main/train_model.py), the difference between the predicted and ground-truth Euler angles is converted into a weighting factor using a cosine penalty. This factor multiplies the landmark loss, forcing the model to pay more attention to samples with large pose discrepancies. Consequently, the backbone learns more robust, pose-invariant features.

### Where is the auxiliary network defined in the PFLD codebase?

The auxiliary network is defined in **[`model2.py`](https://github.com/guoqiangqi/pfld/blob/main/model2.py)** within the `create_model` function. It branches from the `features['auxiliary_input']` tensor, which is captured after the `block3_5` layer (line 887). The network architecture consists of Slim convolution and fully-connected layers defined between lines 887 and 936, culminating in the `euler_angles_pre` output.

### Can the auxiliary network be used independently for head-pose estimation?

While the auxiliary network is trained jointly with the landmark detector, it can technically be used to extract head-pose estimates during inference. However, because it shares the backbone with the main task and is optimized for the specific feature representations learned for facial landmarks, its standalone accuracy for general head-pose estimation may be limited compared to dedicated pose estimation models. For best results, it should be used as implemented in `create_model`, which returns both `landmarks_pred` and `euler_angles_pred`.