musicmouse/espmusicmouse/host_driver/C5S3_ChordRec_HMM.ipynb

663 lines
216 KiB
Plaintext
Raw Permalink Normal View History

2023-01-03 20:49:14 +01:00
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div>\n",
"<a href=\"http://www.music-processing.de/\"><img style=\"float:left;\" src=\"../data/FMP_Teaser_Cover.png\" width=40% alt=\"FMP\"></a>\n",
"<a href=\"https://www.audiolabs-erlangen.de\"><img src=\"../data/Logo_AudioLabs_Long.png\" width=59% style=\"float: right;\" alt=\"AudioLabs\"></a>\n",
"</div>"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div>\n",
"<a href=\"../C5/C5.html\"><img src=\"../data/C5_nav.png\" width=\"100\" style=\"float:right;\" alt=\"C5\"></a>\n",
"<h1>HMM-Based Chord Recognition</h1> \n",
"</div>\n",
"\n",
"<br/>\n",
"\n",
"<p>\n",
"Following Section 5.3.4 of <a href=\"http://www.music-processing.de/\">[Müller, FMP, Springer 2015]</a>, we discuss in this notebook an HMM-based approach for chord recognition. The idea of using HMMs for chord recognition was originally introduced by Sheh and Ellis. \n",
"<ul>\n",
"\n",
"<li><span style=\"color:black\">\n",
"Alexander Sheh, Daniel P. W. Ellis: <strong>Chord segmentation and recognition using EM-trained hidden Markov models.</strong> Proceedings of the International Conference on Music Information Retrieval (ISMIR), Baltimore, 2003. \n",
"<br> \n",
"<a type=\"button\" class=\"btn btn-default btn-xs\" target=\"_blank\" href=\"../data/bibtex/FMP_bibtex_ShehE03_Chord_ISMIR.txt\"> Bibtex </a>\n",
"</span></li>\n",
" \n",
"<li><span style=\"color:black\">\n",
"Taemin Cho, Juan Pablo Bello: <strong>On the Relative Importance of Individual Components of Chord Recognition Systems.</strong> IEEE/ACM Transactions on Audio, Speech, and Language Processing, 22 (2014), pp. 466&ndash;492. \n",
"<br> \n",
"<a type=\"button\" class=\"btn btn-default btn-xs\" target=\"_blank\" href=\"../data/bibtex/FMP_bibtex_ChoB14_Chord_IEEE-TASLP.txt\"> Bibtex </a>\n",
"</span></li>\n",
" \n",
"<li><span style=\"color:black\">\n",
"Nanzhu Jiang, Peter Grosche, Verena Konz, Meinard Müller: <strong>Analyzing Chroma Feature Types for Automated Chord Recognition.</strong> Proceedings of the AES Conference on Semantic Audio, Ilmenau, Germany, 2011. \n",
"<br> \n",
"<a type=\"button\" class=\"btn btn-default btn-xs\" target=\"_blank\" href=\"../data/bibtex/FMP_bibtex_JiangGKM11_Chord_AES.txt\"> Bibtex </a>\n",
"</span></li>\n",
"\n",
"\n",
" \n",
"</ul> \n",
" \n",
" \n",
"</p> "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Introduction\n",
"\n",
"We now show how the concept of [HMMs](../C5/C5S3_HiddenMarkovModel.html) can be applied to improve [automated chord recognition](../C5/C5S2_ChordRec_Templates.html). First of all, we need to create an HMM that suitably models our chord recognition problem. Generally, as introduced in the [FMP noteboook on HMMs](../C5/C5S3_HiddenMarkovModel.html), an HMM is specified by the parameters $\\Theta:=(\\mathcal{A},A,C,\\mathcal{B},B)$. In the chord recognition context, the set \n",
"\n",
"$$\n",
"\\mathcal{A}:=\\{\\alpha_{1},\\alpha_{2},\\ldots,\\alpha_{I}\\}.\n",
"$$\n",
"\n",
"of states is used to model the various chord types that are allowed in the recognition problem. As in the [FMP notebook on template-based chord recognition](../C5/C5S2_ChordRec_Templates.html), we consider in this notebook only the twelve major and twelve minor triads, thus setting\n",
"\n",
"\\begin{equation}\n",
"\\label{eq:ChordReco:HMM:App:Spec:SetStates}\n",
" \\mathcal{A} = \\{\\mathbf{C},\\mathbf{C}^\\sharp,\\ldots,\\mathbf{B},\\mathbf{Cm},\\mathbf{Cm^\\sharp},\\ldots,\\mathbf{Bm}\\} \n",
"\\end{equation}\n",
"\n",
"In this case, the HMM consists of $I=24$ states, which we enumerate as indicated above. For example, $\\alpha_{1}$ corresponds to $\\mathbf{C}$ and $\\alpha_{13}$ to $\\mathbf{Cm}$. In the remainder of this notebook, we do the following: \n",
"\n",
"* First, we explain how to explicitly create an HMM by specifying the other HMM parameters in a musically informed fashion. Even though these parameters may be learned automatically from training data using the [Baum&ndash;Welch Algorithm](../C5/C5S3_HiddenMarkovModel.html), the manual specification of HMM parameters is instructive and leads to an HMM with an explicit musical meaning. \n",
"\n",
"* Second, we apply this HMM for chord recognition. The input (i.e., observation sequence) of the HMM is a [**chromagram representation**](../C3/C3S1_SpecLogFreq-Chromagram.html) of the music recording. Applying the [**Viterbi Algorithm**](../C5/C5S3_Viterbi.html), we then derive an optimal state sequence (consisting of chord labels) that best explains the chroma sequence. The sequence of chord labels yields our frame-wise chord recognition result.\n",
"\n",
"We will compare the HMM-based chord recognition results with the results obtained from the [template-based approach](../C5/C5S2_ChordRec_Templates.html). In particular we will see that the HMM transition model introduces a kind of **context-aware postfiltering**. "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Specification of Emission Likelihoods\n",
"\n",
"In our chord recognition scenario, the observations are chroma vectors that have previously been extracted from the given audio recording. In other words, the observations are $12$-dimensional real-valued vectors which are elements of the continuous feature space $\\mathcal{F}=\\mathbb{R}^{12}$. So far, we have only considered the case of **discrete HMMs**, where the observations are discrete symbols coming from a finite output space $\\mathcal{B}$. To make discrete HMMs applicable to our scenario, one possible procedure is to introduce a finite set of prototype vectors, a so-called **codebook**. Such a codebook can be regarded as a discretization of the continuous feature space $\\mathcal{F}=\\mathbb{R}^{12}$, where each codebook vector represents an entire range of feature vectors. Emission probabilities can then be determined on the basis of this finite set of codebook vectors.\n",
"\n",
"As an alternative, we use in the following an HMM variant, where we replace the discrete output space $\\mathcal{B}$ by the continuous feature space $\\mathcal{F}=\\mathbb{R}^{12}$ and the emission probability matrix $B$ by **likelihood functions**. In particular, the emission probability of a given state is replaced by a normalized similarity value defined as inner product of a state-dependent normalized template and a normalized observation (chroma) vector. To compute the state-dependent likelihood functions, we proceed as described in the [FMP notebook on template-based chord recognition](../C5/C5S2_ChordRec_Templates.html). Let $s:\\mathcal{F} \\times \\mathcal{F} \\to [0,1]$ be the **similarity measure** defined by the [inner product of normalized chroma vectors](../C5/C5S2_ChordRec_Templates.html) (where one should use a [thresholded normalization](../C3/C3S1_FeatureNormalization.html) to avoid division by zero):\n",
"\n",
"$$\n",
"s(x, y) = \\frac{\\langle x,y\\rangle}{\\|x\\|_2\\cdot\\|y\\|_2}\n",
"$$ \n",
"\n",
"for $x,y\\in\\mathcal{F}$. Based on $I=24$ major and minor triads (encoded by the states $\\mathcal{A}$ and indexed by the set $[1:I]$), we consider the [**binary chord templates**](../C5/C5S2_ChordRec_Templates.html) $\\mathbf{t}_i\\in \\mathcal{F}$ for $i\\in [1:I]$. Then, we define the state-dependent likelihood function $b_i:\\mathcal{F}\\to [0,1]$ by\n",
"\n",
"$$\n",
"b_i(x) := \\frac{s(x, \\mathbf{t}_i)}{\\sum_{j\\in[1:I]}s(x, \\mathbf{t}_j)}\n",
"$$\n",
"\n",
"for $x\\in\\mathcal{F}$ and $i\\in [1:I]$. In our scenario, the [observation sequence](../C5/C5S3_HiddenMarkovModel.html) $O=(o_{1},o_{2},\\ldots,o_{N})$ is a sequence of chroma vectors $o_n\\in\\mathcal{F}$. We define the observation-dependent $(I\\times N)$-matrix $B[O]$ by \n",
"\n",
"$$\n",
"B[O](i,n) = b_i(o_n)\n",
"$$\n",
"\n",
"for $i\\in[1:I]$ and $n\\in[1:N]$. Note that his matrix is exactly the [**chord similarity matrix**](../C5/C5S2_ChordRec_Templates.html) with a column-wise [$\\ell^1$-normalization](../C3/C3S1_FeatureNormalization.html), as introduced in the [FMP notebook on template-based chord recognition](../C5/C5S2_ChordRec_Templates.html) and visualized in form of a **time&ndash;chord representation**. In context of the the [Viterbi algorithm](../C5/C5S3_Viterbi.html), the likelihood $B[O](i,n)$ is used to replace the probability value $b_{ik_n}$. \n",
"\n",
"As our running example throughout the remainder of this notebook, we continue our Bach example introduced in the [FMP notebook on chord recognition evaluation](../C5/C5S2_ChordRec_Eval.html). In this example, we consider a piano recording of the first four measures of Johann Sebastian Bach's $\\mathrm{C}$-major prelude. Furthermore, in the next code cell, we show the observation sequence $O$ (chromagram representation) as well as the likelihood matrix $B[O]$ (time&ndash;chord representation).\n",
"\n",
"<img src=\"../data/C5/FMP_C5_F20a.png\" width=\"500px\" align=\"left\" alt=\"FMP_C5_20a\">\n",
"\n",
"<br clear=\"all\" />\n",
"\n",
"<audio style=\"width: 500px;\" src=\"../data/C5/FMP_C5_F20_Bach_BWV846-mm1-4_Fischer.wav\" type=\"audio/mpeg\" controls=\"controls\"></audio>"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/home/martin/code/musicmouse/espmusicmouse/venv/lib/python3.8/site-packages/librosa/core/audio.py:165: UserWarning: PySoundFile failed. Trying audioread instead.\n",
" warnings.warn(\"PySoundFile failed. Trying audioread instead.\")\n"
]
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 648x504 with 6 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"import os\n",
"import numpy as np\n",
"from matplotlib import pyplot as plt\n",
"import pandas as pd\n",
"from scipy.linalg import circulant\n",
"from numba import jit\n",
"\n",
"import sys\n",
"sys.path.append('..')\n",
"import libfmp.b\n",
"from libfmp.c5 import get_chord_labels\n",
"%matplotlib inline\n",
"\n",
"test_file = \"/home/martin/Music/deemix Music/Simone Sommerland - Ki-Ka-Kinderturnen.mp3\"\n",
"\n",
"\n",
"# Specify \n",
"fn_wav = test_file #os.path.join('..', 'data', 'C5', 'FMP_C5_F20_Bach_BWV846-mm1-4_Fischer.wav')\n",
"#fn_ann = os.path.join('..', 'data', 'C5', 'FMP_C5_F20_Bach_BWV846-mm1-4_Fischer_ChordAnnotations.csv')\n",
"color_ann = {'C': [1, 0.5, 0, 1], 'G': [0, 1, 0, 1], 'Dm': [1, 0, 0, 1], 'N': [1, 1, 1, 1]}\n",
"\n",
"N = 4096\n",
"H = 1024\n",
"X, Fs_X, x, Fs, x_dur = \\\n",
" libfmp.c5.compute_chromagram_from_filename(fn_wav, N=N, H=H, gamma=0.1, version='STFT')\n",
"N_X = X.shape[1]\n",
"\n",
"# Chord recogntion\n",
"chord_sim, chord_max = libfmp.c5.chord_recognition_template(X, norm_sim='1')\n",
"chord_labels = libfmp.c5.get_chord_labels(nonchord=False)\n",
"\n",
"# Annotations\n",
"chord_labels = libfmp.c5.get_chord_labels(ext_minor='m', nonchord=False)\n",
"#ann_matrix, ann_frame, ann_seg_frame, ann_seg_ind, ann_seg_sec = \\\n",
"# libfmp.c5.convert_chord_ann_matrix(fn_ann, chord_labels, Fs=Fs_X, N=N_X, last=True)\n",
"#P, R, F, TP, FP, FN = libfmp.c5.compute_eval_measures(ann_matrix, chord_max)\n",
"\n",
"# Plot\n",
"cmap = libfmp.b.compressed_gray_cmap(alpha=1, reverse=False)\n",
"fig, ax = plt.subplots(3, 2, gridspec_kw={'width_ratios': [1, 0.03], \n",
" 'height_ratios': [1.5, 3, 0.2]}, figsize=(9, 7))\n",
"\n",
"libfmp.b.plot_chromagram(X, ax=[ax[0, 0], ax[0, 1]], Fs=Fs_X, clim=[0, 1], xlabel='',\n",
" title='Observation sequence (chromagram with feature rate = %0.1f Hz)' % (Fs_X))\n",
"#libfmp.b.plot_segments_overlay(ann_seg_sec, ax=ax[0, 0], time_max=x_dur,\n",
"# print_labels=False, colors=color_ann, alpha=0.1)\n",
"\n",
"libfmp.b.plot_matrix(chord_sim, ax=[ax[1, 0], ax[1, 1]], Fs=Fs_X, clim=[0, np.max(chord_sim)],\n",
" title='Likelihood matrix (timechord representation)',\n",
" ylabel='Chord', xlabel='')\n",
"ax[1, 0].set_yticks(np.arange(len(chord_labels)))\n",
"ax[1, 0].set_yticklabels(chord_labels)\n",
"#libfmp.b.plot_segments_overlay(ann_seg_sec, ax=ax[1, 0], time_max=x_dur,\n",
"# print_labels=False, colors=color_ann, alpha=0.1)\n",
"\n",
"#libfmp.b.plot_segments(ann_seg_sec, ax=ax[2, 0], time_max=x_dur, time_label='Time (seconds)',\n",
"# colors=color_ann, alpha=0.3)\n",
"ax[2,1].axis('off')\n",
"plt.tight_layout()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Specification of Transition Probabilities\n",
"\n",
"In music, certain chord transitions are more likely than others. This observation is our main motivation to employ [HMMs](../C5/C5S3_HiddenMarkovModel.html), where the first-order temporal relationships between the various chords can be captured by the **transition probability matrix** $A$. In the following, we use the notation $\\alpha_{i}\\rightarrow\\alpha_{j}$ to refer to the transition from state $\\alpha_{i}$ to state $\\alpha_{j}$ for $i,j\\in[1:I]$. For example, the coefficient $a_{1,2}$ expresses the probability for the transition $\\alpha_{1}\\rightarrow\\alpha_{2}$ (corresponding to $\\mathbf{C}\\rightarrow\\mathbf{C}^\\sharp$), whereas $a_{1,8}$ expresses the probability for $\\alpha_{1}\\rightarrow\\alpha_{8}$ (corresponding to $\\mathbf{C}\\rightarrow\\mathbf{G}$). In real music, the change from a tonic to the dominant is much more likely than changing by one semitone, so that the probability $a_{1,8}$ should be much larger than $a_{1,2}$. The coefficients $a_{i,i}$ express the probability of staying in state $\\alpha_{i}$ (i.e., $\\alpha_{i}\\rightarrow\\alpha_{i}$) for $i\\in[1:I]$. These coefficients are also referred to as **self-transition** probabilities.\n",
"\n",
"A transition probability matrix can be specified in many ways. For example, the matrix may be defined manually by a music expert based on rules from harmony theory. The most common approach is to generate such a matrix automatically \n",
"by estimating the transition probabilities from labeled data. In the following figure, we show three different transition matrices (using a log probability scale for visualization purposes). \n",
"\n",
"* The first one was learned from labeled training data based on the [Beatles collection](../C5/C5S3_ChordRec_Beatles.html) using bigrams (pairs of adjacent elements) in the labeled frame sequences. As an example, the coefficient $a_{1,8}$ (corresponding to the transition $\\mathbf{C}\\rightarrow\\mathbf{G}$) has been highlighted. \n",
"* The second matrix is a **transposition-invariant** transition probability matrix obtained from the previous matrix. To achieve transposition invariance, the labeled training dataset is augmented by considering all twelve possible [cyclic chroma shifts](../C3/C3S1_TranspositionTuning.html) to the considered bigrams. \n",
"* The third matrix is a **uniform** transition probability matrix with a large value on the main diagonal (self-transitions) and a much smaller value at all remaining positions. \n",
"\n",
"<img src=\"../data/C5/FMP_C5_F29-30-32.png\" width=\"900px\" align=\"left\" alt=\"FMP_C5_F29-30-32\">\n",
"\n",
"<br clear=\"all\" />\n",
"\n",
"For more details on the construction of these transition matrices, we refer to Section 5.3.4.2 of <a href=\"http://www.music-processing.de/\">[Müller, FMP, Springer 2015]</a>. In the following code cell, we read a `CSV`-file that contains the precomputed transition matrix estimated on the basis of the [Beatles collection](../C5/C5S3_ChordRec_Beatles.html). In the visualization, we show both the probability values as well as the log-probability values. "
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"ename": "NameError",
"evalue": "name 'fn_csv' is not defined",
"output_type": "error",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mNameError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m/tmp/ipykernel_23374/3864277426.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m 62\u001b[0m \u001b[0;31m# Load transition matrix estimated on the basis of the Beatles collection\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 63\u001b[0m \u001b[0;31m#fn_csv = os.path.join('..', 'data', 'C5', 'FMP_C5_transitionMatrix_Beatles.csv')\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 64\u001b[0;31m \u001b[0mA_est_df\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_csv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfn_csv\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mdelimiter\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m';'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 65\u001b[0m \u001b[0mA_est\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mA_est_df\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto_numpy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'float64'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 66\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mNameError\u001b[0m: name 'fn_csv' is not defined"
]
}
],
"source": [
"def plot_transition_matrix(A, log=True, ax=None, figsize=(6, 5), title='',\n",
" xlabel='State (chord label)', ylabel='State (chord label)',\n",
" cmap='gray_r', quadrant=False):\n",
" \"\"\"Plot a transition matrix for 24 chord models (12 major and 12 minor triads)\n",
"\n",
" Notebook: C5/C5S3_ChordRec_HMM.ipynb\n",
"\n",
" Args:\n",
" A: Transition matrix\n",
" log: Show log probabilities (Default value = True)\n",
" ax: Axis (Default value = None)\n",
" figsize: Width, height in inches (only used when ax=None) (Default value = (6, 5))\n",
" title: Title for plot (Default value = '')\n",
" xlabel: Label for x-axis (Default value = 'State (chord label)')\n",
" ylabel: Label for y-axis (Default value = 'State (chord label)')\n",
" cmap: Color map (Default value = 'gray_r')\n",
" quadrant: Plots additional lines for C-major and C-minor quadrants (Default value = False)\n",
"\n",
" Returns:\n",
" fig: The created matplotlib figure or None if ax was given.\n",
" ax: The used axes.\n",
" im: The image plot\n",
" \"\"\"\n",
" fig = None\n",
" if ax is None:\n",
" fig, ax = plt.subplots(1, 1, figsize=figsize)\n",
" ax = [ax]\n",
"\n",
" if log is True:\n",
" A_plot = np.log(A)\n",
" cbar_label = 'Log probability'\n",
" clim = [-6, 0]\n",
" else:\n",
" A_plot = A\n",
" cbar_label = 'Probability'\n",
" clim = [0, 1]\n",
" im = ax[0].imshow(A_plot, origin='lower', aspect='equal', cmap=cmap, interpolation='nearest')\n",
" im.set_clim(clim)\n",
" plt.sca(ax[0])\n",
" cbar = plt.colorbar(im)\n",
" ax[0].set_xlabel(xlabel)\n",
" ax[0].set_ylabel(ylabel)\n",
" ax[0].set_title(title)\n",
" cbar.ax.set_ylabel(cbar_label)\n",
"\n",
" chord_labels = get_chord_labels()\n",
" chord_labels_squeezed = chord_labels.copy()\n",
" for k in [1, 3, 6, 8, 10, 11, 13, 15, 17, 18, 20, 22]:\n",
" chord_labels_squeezed[k] = ''\n",
"\n",
" ax[0].set_xticks(np.arange(24))\n",
" ax[0].set_yticks(np.arange(24))\n",
" ax[0].set_xticklabels(chord_labels_squeezed)\n",
" ax[0].set_yticklabels(chord_labels)\n",
"\n",
" if quadrant is True:\n",
" ax[0].axvline(x=11.5, ymin=0, ymax=24, linewidth=2, color='r')\n",
" ax[0].axhline(y=11.5, xmin=0, xmax=24, linewidth=2, color='r')\n",
"\n",
" return fig, ax, im\n",
"\n",
"# Load transition matrix estimated on the basis of the Beatles collection\n",
"#fn_csv = os.path.join('..', 'data', 'C5', 'FMP_C5_transitionMatrix_Beatles.csv')\n",
"A_est_df = pd.read_csv(fn_csv, delimiter=';')\n",
"A_est = A_est_df.to_numpy('float64')\n",
"\n",
"fig, ax = plt.subplots(1, 2, gridspec_kw={'width_ratios': [1, 1], \n",
" 'height_ratios': [1]}, \n",
" figsize=(10, 3.8))\n",
"\n",
"plot_transition_matrix(A_est, log=False, ax=[ax[0]], title='Transition matrix')\n",
"plot_transition_matrix(A_est, ax=[ax[1]], title='Transition matrix with log probabilities')\n",
"plt.tight_layout()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"To obtain the **transposition-invariant transition matrix**, we simulate the [cyclic chroma shifts](../C3/C3S1_TranspositionTuning.html) on the matrix-level by cyclically shifting and averaging the four quadrants (defined by the major-chord and minor-chord regions) of the original matrix. In the visualization, we show the original transition matrix as well as the resulting transposition-invariant transition matrix. "
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 720x273.6 with 4 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"def matrix_circular_mean(A):\n",
" \"\"\"Computes circulant matrix with mean diagonal sums\n",
"\n",
" Notebook: C5/C5S3_ChordRec_HMM.ipynb\n",
"\n",
" Args:\n",
" A (np.ndarray): Square matrix\n",
"\n",
" Returns:\n",
" A_mean (np.ndarray): Circulant output matrix\n",
" \"\"\"\n",
" N = A.shape[0]\n",
" A_shear = np.zeros((N, N))\n",
" for n in range(N):\n",
" A_shear[:, n] = np.roll(A[:, n], -n)\n",
" circ_sum = np.sum(A_shear, axis=1)\n",
" A_mean = circulant(circ_sum) / N\n",
" return A_mean\n",
" \n",
"def matrix_chord24_trans_inv(A):\n",
" \"\"\"Computes transposition-invariant matrix for transition matrix\n",
" based 12 major chords and 12 minor chords\n",
"\n",
" Notebook: C5/C5S3_ChordRec_HMM.ipynb\n",
"\n",
" Args:\n",
" A (np.ndarray): Input transition matrix\n",
"\n",
" Returns:\n",
" A_ti (np.ndarray): Output transition matrix\n",
" \"\"\"\n",
" A_ti = np.zeros(A.shape)\n",
" A_ti[0:12, 0:12] = matrix_circular_mean(A[0:12, 0:12])\n",
" A_ti[0:12, 12:24] = matrix_circular_mean(A[0:12, 12:24])\n",
" A_ti[12:24, 0:12] = matrix_circular_mean(A[12:24, 0:12])\n",
" A_ti[12:24, 12:24] = matrix_circular_mean(A[12:24, 12:24])\n",
" return A_ti\n",
"\n",
"\n",
"A_ti = matrix_chord24_trans_inv(A_est)\n",
"\n",
"fig, ax = plt.subplots(1, 2, gridspec_kw={'width_ratios': [1, 1], \n",
" 'height_ratios': [1]}, \n",
" figsize=(10, 3.8))\n",
"\n",
"plot_transition_matrix(A_est, ax=[ax[0]], quadrant=True, \n",
" title='Transition matrix')\n",
"plot_transition_matrix(A_ti, ax=[ax[1]], quadrant=True, \n",
" title='Transposition-invariant transition matrix')\n",
"plt.tight_layout()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, we provide a function for generating a uniform transition probability matrix. This function has a parameter $p\\in[0,1]$ that determines the probability for self transitions (the value on the main diagonal). The probabilities on the remaining positions are set such that the resulting matrix is a probability matrix (i.e., all rows and columns sum to one)."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 720x273.6 with 4 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"def uniform_transition_matrix(p=0.01, N=24):\n",
" \"\"\"Computes uniform transition matrix\n",
"\n",
" Notebook: C5/C5S3_ChordRec_HMM.ipynb\n",
"\n",
" Args:\n",
" p (float): Self transition probability (Default value = 0.01)\n",
" N (int): Column and row dimension (Default value = 24)\n",
"\n",
" Returns:\n",
" A (np.ndarray): Output transition matrix\n",
" \"\"\"\n",
" off_diag_entries = (1-p) / (N-1) # rows should sum up to 1\n",
" A = off_diag_entries * np.ones([N, N])\n",
" np.fill_diagonal(A, p)\n",
" return A\n",
"\n",
"fig, ax = plt.subplots(1, 2, gridspec_kw={'width_ratios': [1, 1], \n",
" 'height_ratios': [1]}, \n",
" figsize=(10, 3.8))\n",
"\n",
"p = 0.5\n",
"A_uni = uniform_transition_matrix(p)\n",
"plot_transition_matrix(A_uni, ax=[ax[0]], title='Uniform transition matrix (p=%0.2f)' % p)\n",
"p = 0.9\n",
"A_uni = uniform_transition_matrix(p)\n",
"plot_transition_matrix(A_uni, ax=[ax[1]], title='Uniform transition matrix (p=%0.2f)' % p)\n",
"plt.tight_layout()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## HMM-Based Chord Recognition\n",
"\n",
"As discussed before, the free parameters of the HMM-based model can either be learned automatically from the training set or set manually using musical knowledge. Continuing with our Bach example, we now present an experiment that demonstrates the effect of applying HMMs to our chord recognition scenario. We use the following setting:\n",
"\n",
"* As observation sequence $O$, we use a sequence of chroma vectors.\n",
"* As for the transition probability matrix $A$, we simply use a uniform transition matrix. \n",
"* As for the initial state probability vector $C$, we use a uniform distribution.\n",
"* As for the emission probability matrix $B$, we replace them by the likelihood matrix $B[O]$, which is a normalized version of the chord similarity matrix also used for the [template-based chord recognition](../C5/C5S2_ChordRec_Templates.html).\n",
"* The frame-wise chord recognition results is given by the state sequence computed by the [Viterbi algorithm](../C5/C5S3_Viterbi.html). \n",
"\n",
"Using the likelihood matrix $B[O]$ instead of emission probabilities requires a small modification of the original algorithm. In the following code cell, we provide the implementation of this modification using a numerically stable log version. We then compare the HMM-based results with the [template-based approach](../C5/C5S2_ChordRec_Templates.html) showing the [evaluation results in the form of time&ndash;chord visualizations](../C5/C5S2_ChordRec_Eval.html), respectively."
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 648x252 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 648x252 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"@jit(nopython=True)\n",
"def viterbi_log_likelihood(A, C, B_O):\n",
" \"\"\"Viterbi algorithm (log variant) for solving the uncovering problem\n",
"\n",
" Notebook: C5/C5S3_Viterbi.ipynb\n",
"\n",
" Args:\n",
" A (np.ndarray): State transition probability matrix of dimension I x I\n",
" C (np.ndarray): Initial state distribution of dimension I\n",
" B_O (np.ndarray): Likelihood matrix of dimension I x N\n",
"\n",
" Returns:\n",
" S_opt (np.ndarray): Optimal state sequence of length N\n",
" S_mat (np.ndarray): Binary matrix representation of optimal state sequence\n",
" D_log (np.ndarray): Accumulated log probability matrix\n",
" E (np.ndarray): Backtracking matrix\n",
" \"\"\"\n",
" I = A.shape[0] # Number of states\n",
" N = B_O.shape[1] # Length of observation sequence\n",
" tiny = np.finfo(0.).tiny\n",
" A_log = np.log(A + tiny)\n",
" C_log = np.log(C + tiny)\n",
" B_O_log = np.log(B_O + tiny)\n",
"\n",
" # Initialize D and E matrices\n",
" D_log = np.zeros((I, N))\n",
" E = np.zeros((I, N-1)).astype(np.int32)\n",
" D_log[:, 0] = C_log + B_O_log[:, 0]\n",
"\n",
" # Compute D and E in a nested loop\n",
" for n in range(1, N):\n",
" for i in range(I):\n",
" temp_sum = A_log[:, i] + D_log[:, n-1]\n",
" D_log[i, n] = np.max(temp_sum) + B_O_log[i, n]\n",
" E[i, n-1] = np.argmax(temp_sum)\n",
"\n",
" # Backtracking\n",
" S_opt = np.zeros(N).astype(np.int32)\n",
" S_opt[-1] = np.argmax(D_log[:, -1])\n",
" for n in range(N-2, -1, -1):\n",
" S_opt[n] = E[int(S_opt[n+1]), n]\n",
"\n",
" # Matrix representation of result\n",
" S_mat = np.zeros((I, N)).astype(np.int32)\n",
" for n in range(N):\n",
" S_mat[S_opt[n], n] = 1\n",
"\n",
" return S_mat, S_opt, D_log, E\n",
"\n",
"A = uniform_transition_matrix(p=0.5)\n",
"C = 1 / 24 * np.ones((1, 24))\n",
"B_O = chord_sim\n",
"chord_HMM, _, _, _ = viterbi_log_likelihood(A, C, B_O)\n",
"\n",
"P, R, F, TP, FP, FN = libfmp.c5.compute_eval_measures(ann_matrix, chord_HMM)\n",
"title = 'HMM-Based approach (N=%d, TP=%d, FP=%d, FN=%d, P=%.2f, R=%.2f, F=%.2f)' % (N_X, TP, FP, FN, P, R, F)\n",
"fig, ax, im = libfmp.c5.plot_matrix_chord_eval(ann_matrix, chord_HMM, Fs=1, \n",
" title=title, ylabel='Chord', xlabel='Time (frames)', chord_labels=chord_labels)\n",
"plt.tight_layout()\n",
"plt.show()\n",
"\n",
"P, R, F, TP, FP, FN = libfmp.c5.compute_eval_measures(ann_matrix, chord_max)\n",
"title = 'Template-based approach (N=%d, TP=%d, FP=%d, FN=%d, P=%.2f, R=%.2f, F=%.2f)' %\\\n",
" (N_X, TP, FP, FN, P, R, F)\n",
"fig, ax, im = libfmp.c5.plot_matrix_chord_eval(ann_matrix, chord_max, Fs=1, \n",
" title=title, ylabel='Chord', xlabel='Time (frames)', chord_labels=chord_labels)\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this example, the HMM-based chord recognizer clearly outperforms the template-based approach. The improvements in HMM-based approach come specifically from the **transition model** that introduces context-sensitive smoothing. In the case of **high self-transition probabilities**, a chord recognizer tends to stay in the current chord rather than change to another one, which can be regarded as a kind of smoothing. This effect is also demonstrated in our Bach example, where the broken chords cause many [chord ambiguities](../C5/C5S2_ChordRec_Eval.html) of short duration. This leads to many random-like chord changes when using a simple template-based chord recognizer. Using an HMM-based approach, chord changes are only performed when the relatively low transition probabilities are compensated by a substantial increase of emission probabilities. Consequently, only the dominant chord changes remain."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Prefiltering vs. Postfiltering\n",
"\n",
"In the [FMP notebook on chord recognition evaluation](../C5/C5S2_ChordRec_Eval.html), we showed for the Bach example that one may achieve similar improvements by applying a longer window size when computing the input chromagram. Applying longer window sizes more or less amounts to temporal smoothing of the observation sequence. Since this smoothing is performed **prior** to the pattern matching step, we also call this strategy **prefiltering**. Note that such a prefiltering step not only smoothes out noise-like frames, but also washes out characteristic chroma information and blurs transitions. As opposed to prefiltering, the HMM-based approach leaves the feature representation untouched. Furthermore, the smoothing is performed in combination with the pattern matching step. For this reason, we also call this approach **postfiltering**. As a result, the original chroma information is preserved and transitions in the feature representation are kept sharp. \n",
"\n",
"<img src=\"../data/C5/FMP_C5_F13.png\" width=\"500px\" align=\"middle\" alt=\"FMP_C5_F13\">"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Further Notes\n",
"\n",
"In this notebook, we introduced a basic HMM-based approach for chord recognition. In our simplistic model, we used $24$ states that correspond to the $12$ major and $12$ minor triads and fixed the HMM parameters explicitly using musical knowledge. \n",
"\n",
"* In the application to chord labeling, the HMM was then used to uncover the most likely chord labeling sequence that generates a given sequence of chroma features&mdash;an idea originally introduced by [Sheh and Ellis](https://www.ee.columbia.edu/~dpwe/pubs/ismir03-chords.pdf). \n",
"\n",
"* In general, there is a delicate interplay of the various feature extraction, filtering, and pattern matching components composing a chord recognition system. In this context, we refer to the excellent overview paper [On the Relative Importance of Individual Components of Chord Recognition Systems](https://ieeexplore.ieee.org/document/6691936) by Cho and Bello. \n",
"\n",
"* In the article [Analyzing Chroma Feature Types for Automated Chord Recognition](https://secure.aes.org/forum/pubs/conferences/?elib=15943) by Jiang et al., the importance of the input representation is investigated.\n",
"\n",
"In this book, we have only considered a basic HMM variant. There are many more variants and extensions of HMMs including continuous HMMs and HMMs with specific state transition topologies. Rather than fixing the model parameters manually, the power of general HMMs is to automatically learn the free parameters based on training examples (e.g., using the [Baum-Welch Algorithm](../C5/C5S3_HiddenMarkovModel.html)). The estimation of the model parameters can become very intricate, leading to challenging and deep mathematical problems. For an excellent textbook on the classical theory of HMMs, including the discrete as well as the continuous case, we refer to the excellent book on [Hidden Markov Models for Speech Recognition](https://dl.acm.org/doi/book/10.5555/575447) by Huang et al. (1990). We close this notebook with an overview of a typical HMM-based chord recognition approach consisting of a training and an evaluation stage.\n",
"\n",
"<img src=\"../data/C5/FMP_C5_F33.png\" width=\"600px\" align=\"center\" alt=\"FMP_C5_F33.png\">\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<div class=\"alert\" style=\"background-color:#F5F5F5; border-color:#C8C8C8\">\n",
"<strong>Acknowledgment:</strong> This notebook was created by <a href=\"https://www.audiolabs-erlangen.de/fau/professor/mueller\">Meinard Müller</a> and <a href=\"https://www.audiolabs-erlangen.de/fau/assistant/weiss\">Christof Weiß</a>.\n",
"</div> "
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<table style=\"border:none\">\n",
"<tr style=\"border:none\">\n",
" <td style=\"min-width:50px; border:none\" bgcolor=\"white\"><a href=\"../C0/C0.html\"><img src=\"../data/C0_nav.png\" style=\"height:50px\" alt=\"C0\"></a></td>\n",
" <td style=\"min-width:50px; border:none\" bgcolor=\"white\"><a href=\"../C1/C1.html\"><img src=\"../data/C1_nav.png\" style=\"height:50px\" alt=\"C1\"></a></td>\n",
" <td style=\"min-width:50px; border:none\" bgcolor=\"white\"><a href=\"../C2/C2.html\"><img src=\"../data/C2_nav.png\" style=\"height:50px\" alt=\"C2\"></a></td>\n",
" <td style=\"min-width:50px; border:none\" bgcolor=\"white\"><a href=\"../C3/C3.html\"><img src=\"../data/C3_nav.png\" style=\"height:50px\" alt=\"C3\"></a></td>\n",
" <td style=\"min-width:50px; border:none\" bgcolor=\"white\"><a href=\"../C4/C4.html\"><img src=\"../data/C4_nav.png\" style=\"height:50px\" alt=\"C4\"></a></td>\n",
" <td style=\"min-width:50px; border:none\" bgcolor=\"white\"><a href=\"../C5/C5.html\"><img src=\"../data/C5_nav.png\" style=\"height:50px\" alt=\"C5\"></a></td>\n",
" <td style=\"min-width:50px; border:none\" bgcolor=\"white\"><a href=\"../C6/C6.html\"><img src=\"../data/C6_nav.png\" style=\"height:50px\" alt=\"C6\"></a></td>\n",
" <td style=\"min-width:50px; border:none\" bgcolor=\"white\"><a href=\"../C7/C7.html\"><img src=\"../data/C7_nav.png\" style=\"height:50px\" alt=\"C7\"></a></td>\n",
" <td style=\"min-width:50px; border:none\" bgcolor=\"white\"><a href=\"../C8/C8.html\"><img src=\"../data/C8_nav.png\" style=\"height:50px\" alt=\"C8\"></a></td>\n",
"</tr>\n",
"</table>"
]
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 4
}