emotion2vec

repository·main·Indexed 22 days ago

https://github.com/ddlbojack/emotion2vec

A framework for self-supervised pre-training of speech emotion representations. It includes a universal representation model (emotion2vec) and a series of foundation models for speech emotion recognition (emotion2vec+), including seed, base, and large variants. The framework supports 9-class emotion classification and can be used via the FunASR interface or installed from source. It provides tools for extracting utterance- or frame-level features and includes scripts for training downstream models on the IEMOCAP dataset.

Tokens
3.1K
Snippets
10
Records
12
Agent score
78%

What's inside emotion2vec

  1. Overview of emotion2vec+ model variants

    main

    The emotion2vec+ series provides different scales of foundation models for speech emotion recognition:

    • emotion2vec+ seed: Fine-tuned with academic speech emotion data from EmoBox.
    • emotion2vec+ base: Fine-tuned with filtered large-scale pseudo-labeled data (~90M parameters).
    • emotion2vec+ large: Fine-tuned with filtered large-scale pseudo-labeled data (~300M parameters).
  2. Download pre-processed IEMOCAP data

    main

    If you prefer not to process the raw IEMOCAP dataset, you can download the pre-processed training files (train.npy, train.lengths, and train.emo) directly via Google Drive or Baidu Netdisk.

    # train.npy
    # Google Drive: https://drive.google.com/file/d/1WI9rM9v-WIBKhzDRHWwkgvJNHY1ZkEhd/view?usp=sharing
    # Baidu Netdisk: https://pan.baidu.com/s/1kWpT2X5gVc6pYULN0WdSdg?pwd=hsjt (password: hsjt)
    
    # train.lengths
    # Google Drive: https://drive.google.com/file/d/1wnPBKwxz19ucirrdjlvZdhqvjb3efaj2/view?usp=sharing
    # Baidu Netdisk: https://pan.baidu.com/s/1pa7GHnGyTZw_fi-U1y4YgA?pwd=a99c (password: a99c)
    
    # train.emo
    # Google Drive: https://drive.google.com/file/d/1UQZwfGXqCh58XJaaOJsEvJH1cKNXAy-3/view?usp=sharing
    # Baidu Netdisk: https://pan.baidu.com/s/1A_DTIoC7VbTxly5HzUZfpw?pwd=j9xv (password: j9xv)
  3. Install emotion2vec via FunASR

    main

    The recommended way to use emotion2vec (both the original representation model and the newer emotion2vec+ foundation models) is through the funasr interface. This provides a smooth experience for model downloading and inference.

    Install the required package using pip:

    pip install -U funasr
  4. Prepare IEMOCAP features and labels from scratch

    main

    To evaluate emotion2vec on the IEMOCAP dataset, you can generate the necessary features and labels from the raw dataset.

    1. Download the IEMOCAP_full_release from the official website.
    2. Define your local paths for the dataset root, manifest, checkpoints, features, and model directories.
    3. Run the manifest generation script.
    4. Run the feature extraction script using emotion2vec.
    5. Copy the generated train.emo file to your feature path.

    This process produces three files in your feature path: train.npy, train.lengths, and train.emo.

    # Set your own paths
    IEMOCAP_ROOT=/path/to/IEMOCAP_full_release
    manifest_path=/path/to/manifest
    checkpoint_path=/path/to/emotion2vec_ckpt
    feat_path=/path/to/feats
    fairseq_root=/path/to/fairseq_root
    model_path=../upstream
    
    # generate manifest and labels from raw dataset
    bash scripts/iemocap_manifest_and_labels.sh $IEMOCAP_ROOT $manifest_path
    
    # generate features with emotion2vec
    bash scripts/emotion2vec_extract_features.sh $fairseq_root $manifest_path $model_path $checkpoint_path $feat_path
    
    cp $manifest_path/train.emo $feat_path
  5. Install emotion2vec from source code

    main

    If you prefer not to use the FunASR interface, you can install from the source code.

    Requirements:

    • Python >= 3.8
    • PyTorch >= 1.13

    Steps:

    1. Install fairseq.
    2. Clone the repository.
    3. Download the checkpoint from Google Drive, Baidu Netdisk, or ModelScope.
    4. Run the provided extraction script.
    pip install fairseq
    git clone https://github.com/ddlBoJack/emotion2vec.git
  6. Perform speech emotion recognition with emotion2vec+

    main

    The emotion2vec+ models are foundation models for Speech Emotion Recognition (SER). They can classify audio into 9 specific emotion classes.

    When using AutoModel.generate() with an emotion2vec+ model:

    • Set extract_embedding=False to get the 9-class emotion labels and scores.
    • Set extract_embedding=True to get both the emotion labels/scores and the underlying features.

    9-class emotion mapping: 0: angry, 1: disgusted, 2: fearful, 3: happy, 4: neutral, 5: other, 6: sad, 7: surprised, 8: unknown

    Model IDs:

    • iic/emotion2vec_plus_seed
    • iic/emotion2vec_plus_base
    • iic/emotion2vec_plus_large
    • iic/emotion2vec_base_finetuned (Jan. 2024 release)
    from funasr import AutoModel
    
    # Use an emotion2vec+ model for 9-class emotion recognition
    model_id = "iic/emotion2vec_plus_large"
    
    model = AutoModel(
        model=model_id,
        hub="ms",  # "ms" or "modelscope" for China mainland users; "hf" or "huggingface" for other overseas users
    )
    
    wav_file = f"{model.model_path}/example/test.wav"
    # extract_embedding=False returns {'feats', 'labels', 'scores'}
    rec_result = model.generate(wav_file, output_dir="./outputs", granularity="utterance", extract_embedding=False)
    print(rec_result)
  7. Train a downstream model for IEMOCAP

    main

    You can train a downstream model using only linear layers. The provided script uses leave-one-session-out 5-fold cross-validation as an example. Since frame-level features are provided, you can also implement more complex models for better performance.

    feat_path=/path/to/feats
    bash train.sh ${feat_path}/train
  8. Extract speech emotion features with emotion2vec

    main

    To extract universal speech emotion representations (features) using the original emotion2vec model via FunASR, use AutoModel.generate() with the granularity parameter.

    Granularity options:

    • granularity="utterance": Returns a single feature vector per utterance (e.g., {'feats': [*768]}).
    • granularity="frame": Returns frame-level features (e.g., {'feats': [T*768]}).

    Model ID:

    • iic/emotion2vec_base
    from funasr import AutoModel
    
    model_id = "iic/emotion2vec_base"
    model = AutoModel(
        model=model_id,
        hub="ms",  # "ms" or "modelscope" for China mainland users; "hf" or "huggingface" for other overseas users
    )
    
    wav_file = f"{model.model_path}/example/test.wav"
    # Returns {'feats'}
    rec_result = model.generate(wav_file, output_dir="./outputs", granularity="utterance")
    print(rec_result)
  9. Perform emotion inference with BaseModel

    main

    To perform emotion recognition using a fine-tuned BaseModel, you need to load a checkpoint into the model instance and pass feature tensors along with a padding mask.

    1. Initialize the model: Create a BaseModel instance specifying the input_dim (e.g., 768 for emotion2vec features) and the output_dim corresponding to your number of emotion labels.
    2. Load weights: Use torch.load to load your checkpoint and model.load_state_dict() to apply them.
    3. Prepare inputs:
      • feat: A tensor of shape (batch_size, sequence_length, input_dim).
      • padding_mask: A boolean tensor of shape (batch_size, sequence_length) where True indicates padding (though in the example, torch.zeros(...).bool() is used to indicate no padding).
    4. Extract prediction: The model returns logits. Use torch.max(outputs.data, dim=1) to find the index of the highest probability emotion.
    5. Map to label: Use a dictionary to map the predicted index back to a human-readable label (e.g., 'ang', 'hap', 'neu', 'sad').
    import torch
    from model import BaseModel
    
    # 1. Setup label mapping
    label_dict={'ang': 0, 'hap': 1, 'neu': 2, 'sad': 3}
    idx2label = {v: k for k, v in label_dict.items()}
    
    # 2. Initialize and load model
    model = BaseModel(input_dim=768, output_dim=len(label_dict))
    ckpt = torch.load('outputs/2024-01-14/22-57-42/model_1.pth')
    model.load_state_dict(ckpt)
    
    # 3. Prepare dummy input (batch_size=1, seq_len=100, input_dim=768)
    feat = torch.randn(1, 100, 768)
    padding_mask = torch.zeros(1, 100).bool()
    
    # 4. Inference
    outputs = model(feat, padding_mask)
    
    # 5. Get prediction
    _, predict = torch.max(outputs.data, dim=1)
    print(idx2label[predict.item()])
  10. Train downstream models on IEMOCAP via train_iemocap()

    main

    The train_iemocap function serves as the main entrypoint for training downstream emotion recognition models using the IEMOCAP dataset. It uses hydra for configuration management, loading settings from config/default.yaml.

    Key behaviors:

    • Cross-Validation: It performs a 5-fold cross-validation by treating each of the 5 IEMOCAP sessions as a test set sequentially.
    • Data Loading: Uses load_ssl_features to load pre-extracted SSL features and train_valid_test_iemocap_dataloader to create training, validation, and testing loaders.
    • Model Architecture: Initializes a BaseModel with an input dimension of 768 (standard for many SSL models) and an output dimension corresponding to the number of emotion labels.
    • Optimization: Uses RMSprop with a CyclicLR scheduler.
    • Checkpointing: Saves the model state dictionary (.pth) for the best performing epoch based on Validation Weighted Accuracy (WA).
    • Evaluation: Reports Weighted Accuracy (WA), Unweighted Accuracy (UA), and F1 score for both validation and testing phases.
    @hydra.main(config_path='config', config_name='default.yaml')
    def train_iemocap(cfg: DictConfig):
        # Implementation details for training on IEMOCAP
        ...
    
    if __name__ == '__main__':
        train_iemocap()
  11. Count model parameters with count_parameters()

    main

    The count_parameters(model) utility function iterates through a PyTorch model's named parameters, prints the name and number of elements for each parameter, and outputs the total parameter count.

    def count_parameters(model):
        total_params = 0
        for name, parameter in model.named_parameters():
            param = parameter.numel()
            print(f"{name}: {param}")
            total_params += param
        print(f"\nTotal number of parameters: {total_params}")