Skip to content

extract_vgg_feature_maps 🗿

Extract feature maps from VGG-Face elicited by the original and 3D-reconstructed heads in the CFD.

Functions:

Name Description
extract_activation_maps

Extract activation map(s) from model layer(s).

extract_vgg_human_judgment_activation_maps_in_core_bridge

Extract activation map(s) in the VGG-core-bridge from a VGGFaceHumanjudgment[FrozenCore] model.

get_vgg_activation_maps

Get activation maps of the VGG-Face model for a list of head models.

get_vgg_human_judgment_activation_maps

Get activation maps of VGGFaceHumanjudgment[FrozenCore] for a list of head models.

plot_activation_maps

Plot activation maps.

extract_activation_maps 🗿

extract_activation_maps(
    model: VGGFace,
    image_path: str,
    layer_name: str | None = None,
    **kwargs
) -> ndarray | list[ndarray]

Extract activation map(s) from model layer(s).

Source code in code/facesim3d/modeling/VGG/extract_vgg_feature_maps.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def extract_activation_maps(
    model: VGGFace, image_path: str, layer_name: str | None = None, **kwargs
) -> np.ndarray | list[np.ndarray]:
    """Extract activation map(s) from model layer(s)."""
    model.eval()
    image = load_image_for_model(image_path=image_path, dtype=torch.double, **kwargs)

    # Forward image through VGGFace
    with torch.no_grad():
        _ = model(image)

    if layer_name is None:
        return [l_out.data.cpu().numpy() for l_out in model.layer_output]

    layer_name = layer_name.lower()
    if layer_name not in model.layer_names:
        msg = f"Layer name '{layer_name}' not in model.layer_names !"
        raise ValueError(msg)
    return model.layer_output[model.layer_names.index(layer_name)].data.cpu().numpy()

extract_vgg_human_judgment_activation_maps_in_core_bridge 🗿

extract_vgg_human_judgment_activation_maps_in_core_bridge(
    model: (
        VGGFaceHumanjudgment
        | VGGFaceHumanjudgmentFrozenCore
    ),
    model_input: Tensor,
) -> ndarray | list[ndarray]

Extract activation map(s) in the VGG-core-bridge from a VGGFaceHumanjudgment[FrozenCore] model.

Source code in code/facesim3d/modeling/VGG/extract_vgg_feature_maps.py
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
def extract_vgg_human_judgment_activation_maps_in_core_bridge(
    model: VGGFaceHumanjudgment | VGGFaceHumanjudgmentFrozenCore,
    model_input: torch.Tensor,
) -> np.ndarray | list[np.ndarray]:
    """Extract activation map(s) in the `VGG-core-bridge` from a `VGGFaceHumanjudgment[FrozenCore]` model."""
    model.eval()

    # Forward image through VGGFaceHumanjudgment[FrozenCore]
    with torch.no_grad():
        if model.parallel_bridge:
            # Push the same image through all three parts of the bridge
            out = [
                model.forward_vgg(x=model_input, bridge_idx=1).data.cpu().numpy(),
                model.forward_vgg(x=model_input, bridge_idx=2).data.cpu().numpy(),
                model.forward_vgg(x=model_input, bridge_idx=3).data.cpu().numpy(),
            ]
        else:
            out = model.forward_vgg(x=model_input, bridge_idx=None).data.cpu().numpy()

    return out

get_vgg_activation_maps 🗿

get_vgg_activation_maps(
    list_of_head_nrs: list[str | int],
    layer_name: str,
    data_mode: str,
) -> DataFrame

Get activation maps of the VGG-Face model for a list of head models.

Source code in code/facesim3d/modeling/VGG/extract_vgg_feature_maps.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
def get_vgg_activation_maps(list_of_head_nrs: list[str | int], layer_name: str, data_mode: str) -> pd.DataFrame:
    """Get activation maps of the `VGG-Face` model for a list of head models."""
    layer_name = layer_name.lower()
    vgg_model = get_vgg_face_model(save_layer_output=True)
    vgg_model.eval()
    if layer_name not in vgg_model.layer_names:
        msg = f"Layer name '{layer_name}' not in model."
        raise ValueError(msg)

    df_activation_maps = None  # init
    for head_nr in tqdm(
        list_of_head_nrs, desc=f"Extracting activation maps in layer '{layer_name}'", colour="#F79F09"
    ):
        p2_img = face_image_path(head_id=head_nr, data_mode=data_mode, return_head_id=False)
        act_map = extract_activation_maps(model=vgg_model, image_path=p2_img, layer_name=layer_name)
        act_map = act_map.flatten()

        if df_activation_maps is None:
            m_len = len(act_map)
            df_activation_maps = pd.DataFrame(
                index=list_of_head_nrs, columns=[f"{layer_name.upper()}-{i: 0{oom(m_len) + 1}d}" for i in range(m_len)]
            )

        df_activation_maps.loc[head_nr, :] = act_map.astype("float32")

    return df_activation_maps

get_vgg_human_judgment_activation_maps 🗿

get_vgg_human_judgment_activation_maps(
    list_of_head_nrs: list[str | int] | Series,
    session: str,
    model_name: str,
    data_mode: str,
    exclusive_gender_trials: str | None = None,
) -> DataFrame

Get activation maps of VGGFaceHumanjudgment[FrozenCore] for a list of head models.

Source code in code/facesim3d/modeling/VGG/extract_vgg_feature_maps.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
def get_vgg_human_judgment_activation_maps(
    list_of_head_nrs: list[str | int] | pd.Series,
    session: str,
    model_name: str,
    data_mode: str,
    exclusive_gender_trials: str | None = None,
) -> pd.DataFrame:
    """Get activation maps of `VGGFaceHumanjudgment[FrozenCore]` for a list of head models."""
    # Get model
    vgg_hj_model = load_trained_vgg_face_human_judgment_model(
        session=session,
        model_name=model_name,
        exclusive_gender_trials=exclusive_gender_trials,
        device="gpu" if torch.cuda.is_available() else "cpu",
    )
    vgg_hj_model.eval()

    # Get model data
    full_dataset_dl, _, _ = prepare_data_for_human_judgment_model(
        session=session,
        frozen_core=vgg_hj_model.freeze_vgg_core,
        data_mode=data_mode,
        last_core_layer=vgg_hj_model.last_core_layer,
        split_ratio=(1.0, 0.0, 0.0),  # push all data in one set
        batch_size=1,
        num_workers=1,
        dtype=torch.float32,
        exclusive_gender_trials=exclusive_gender_trials,
    )

    df_activation_maps = None  # init
    for i, i_data in tqdm(
        enumerate(full_dataset_dl),
        desc="Extracting activation maps in layer 'vgg_core_bridge'",
        total=len(full_dataset_dl),
        colour="#F79F09",
    ):
        ipt_1, ipt_2, ipt_3, _, idx = i_data.values()  # _ == choice
        head_nr_1, head_nr_2, head_nr_3 = (
            f"Head{nr}"
            for nr in full_dataset_dl.dataset.dataset.session_data.iloc[idx.item()][["head1", "head2", "head3"]]
        )

        for head_nr, model_input in zip([head_nr_1, head_nr_2, head_nr_3], [ipt_1, ipt_2, ipt_3], strict=True):
            if head_nr not in (
                list_of_head_nrs.to_numpy() if hasattr(list_of_head_nrs, "to_numpy") else list_of_head_nrs
            ):
                # Skip if the head is not in the list
                continue

            if df_activation_maps is not None and not df_activation_maps.loc[head_nr].isna().any():
                # Skip if this is already computed
                continue

            act_map = extract_vgg_human_judgment_activation_maps_in_core_bridge(
                model=vgg_hj_model, model_input=model_input
            )
            if isinstance(act_map, list):
                act_map = np.concatenate(act_map)
            act_map = act_map.flatten()
            if df_activation_maps is None:
                m_len = len(act_map)
                df_activation_maps = pd.DataFrame(
                    index=list_of_head_nrs, columns=[f"VGG_CORE_BRIDGE-{i: 0{oom(m_len) + 1}d}" for i in range(m_len)]
                )

            df_activation_maps.loc[head_nr, :] = act_map.astype("float32")

    return df_activation_maps

plot_activation_maps 🗿

plot_activation_maps(
    activation_maps: ndarray | list[ndarray],
    layer_names: str | list[str],
)

Plot activation maps.

Source code in code/facesim3d/modeling/VGG/extract_vgg_feature_maps.py
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
def plot_activation_maps(activation_maps: np.ndarray | list[np.ndarray], layer_names: str | list[str]):
    """Plot activation maps."""
    # Convert to list (if necessary)
    if isinstance(activation_maps, np.ndarray):
        activation_maps = [activation_maps]
    if isinstance(layer_names, str):
        layer_names = [layer_names]

    # Pre-process activation maps for plotting
    processed_layer_outputs = []
    for lout in activation_maps:
        feature_map = lout.squeeze(0)
        gray_scale = np.sum(feature_map, axis=0)
        gray_scale /= feature_map.shape[0]
        processed_layer_outputs.append(gray_scale)

    # Plot activation maps
    n_plots = len([ln for ln in layer_names if "fc" not in ln])
    if n_plots < len(layer_names):
        print(f"Some layers are fully connected (fc) layers (n={len(layer_names) - n_plots}) & will not be plotted.")
    size_r, size_c = get_n_cols_and_rows(n_plots=n_plots, square=True)
    fig = plt.figure(figsize=(9, 9))
    for i, p_lout in enumerate(processed_layer_outputs):
        if "fc" in layer_names[i]:
            continue
        a = fig.add_subplot(size_r, size_c, i + 1)
        _ = plt.imshow(p_lout)
        a.axis("off")
        a.set_title(layer_names[i], fontsize=12)
    plt.tight_layout()

    # TODO: Save figure  # noqa: FIX002
    pass