Skip to content

About phoneme-level SSL feature #6

Description

@Secondtonumb

Hi, thank you very much for your excellent work — it has been extremely helpful.

I applied the Kaldi GOP method to extract phoneme-level timestamps, and based on these timestamps, I further processed the SSL features as follows:
1. Use GOP-generated timestamps to segment frame-level SSL features.
2. Aggregate frame-level features within each phoneme segment into a chunk.
3. Compute the mean of each chunk as the phoneme-level SSL feature.

def extract_utterance(self, utt_id: str, wav_path: str) -> np.ndarray:
        """
        提取单个 utterance 的音素级特征
        
        Args:
            utt_id: utterance ID
            wav_path: 音频文件路径
        
        Returns:
            (num_phonemes, target_phn_frames, feature_dim) 的特征数组
        """
        # 提取帧级特征
        frame_features = self._load_and_extract_features(wav_path)
        
        # 获取该 utterance 的音素信息
        if utt_id not in self.ctm_data:
            print(f"Warning: {utt_id} not found in CTM file")
            return np.array([], dtype=np.float32)
        
        phones_info = self.ctm_data[utt_id]
        phn_features_list = []
        
        # 对每个音素进行池化
        for phn_info in phones_info:
            start_frame = self._time_to_frame_idx(phn_info['start_time'])
            end_frame = self._time_to_frame_idx(phn_info['end_time'])
            
            # 提取该音素的帧
            start_frame = max(0, start_frame)
            end_frame = min(end_frame, frame_features.shape[0])
            
            if start_frame < end_frame:
                phn_frames = frame_features[start_frame:end_frame]
            else:
                phn_frames = np.array([], dtype=np.float32).reshape(0, self.feature_dim)
            
            # 池化
            pooled = self._pool_frames(phn_frames) # mean here
            phn_features_list.append(pooled)
        
        # 堆叠所有音素特征
        if phn_features_list:
            phn_features = np.stack(phn_features_list, axis=0)  # (num_phonemes, feature_dim)
        else:
            phn_features = np.array([], dtype=np.float32).reshape(0, self.feature_dim)
        
        # Padding
        padded_features = self._pad_features(phn_features)
        return padded_features

However, after doing this, I found that the extracted phoneme-level features differ significantly from those you released and the performance gap is also quite noticeable.

So I would like to ask:
1. Is this chunk-mean aggregation strategy consistent with your actual implementation?
2. Did you perform any additional preprocessing steps on the SSL features before or after phoneme-level aggregation?
3. Do the neighbouring silent frames also count?

This would be extremely helpful if you could help me check these issues.

Here I attached:
• My generated CTM file on test set.
• The corresponding phoneme-level WavLM features extracted using the method above and your release on utt 000030012

feats_and_ctm.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions