Guillaume Frèche

Personal website

Home About Machine learning Signal processing Maths

Python script for weighted RLS applied to speech signals

# Apply weighted Recursive Least Squares to determine autoregressive model of a speech signal

from numpy import sqrt, linspace, zeros, identity, outer, log10
from numpy.random import randn
import matplotlib.pyplot as plt
from scipy.io import wavfile

# Parameters for proper figure export
fig_width_pt = 2*252.0  # Get this from LaTeX using \showthe\columnwidth
inches_per_pt = 1.0/72.27               # Convert pt to inch
golden_mean = (sqrt(5)+1.0)/2.0         # Aesthetic ratio
fig_width = fig_width_pt*inches_per_pt      # height in inches
fig_height = fig_width/golden_mean  # width in inches
fig_size =  [fig_width,fig_height]


# Weighted RLS function
def RLS_autoregressive(data, m, lamb, delta):
    """
        Applies Recursive Least Squares algorithm with forgetting factor
        on autoregressive signals
        
        :param data: the autogressive signal on which the RLS algorithm is applied
        :param m: order of the autoregressive model
        :param lamb: the forgetting factor lambda
        :param delta: factor to initialize matrix P
        :type data: numpy.ndarray
        :type m: int
        :type lamb: float
        :type delta: float
        :return histo_weights: a table keeping track of all the weights estimated
        during the algorithm
        :return error_weights: error between signal sample and estimated sample
    """
    
    # Data length
    n = len(data)
    
    # Initialization of parameters returned by the function
    histo_weights = zeros((n-m,m))
    error_weights = zeros(n-m)
    
    # Recursive Least Squares algorithm
    # Generate estimated weights
    estim_weights = zeros(m)
    
    histo_weights[0,:] = estim_weights
    # Initialize matrix P
    P = identity(m)*delta
    
    for k in range(n-m-1):
        # Convert data
        x = data[k:k+m]
        y = data[k+m]
        
        # Update gain vector g
        denom = lamb + (x @ P @ (x.T))
        g = (P @ (x.T))/denom
        
        # Update weights
        estim_weights = estim_weights + (y - (x @ estim_weights))*g
        histo_weights[k+1,:] = estim_weights
        
        # Update matrix P
        P = (identity(m) - outer(g, x))@P/lamb
        
        # Compute error
        error_weights[k] = (y - (x @ estim_weights))**2
    
    return histo_weights, error_weights


# Read the wav file containing the speech signal
fs, data = wavfile.read('hello.wav')
# Keep the signal between 0 s and 0.6 s
data = data[:26460,0]
# Time variable
t = linspace(0,len(data)/fs,len(data))

# Plot and save the speech signal
plt.close("all")
fig1, ax1 = plt.subplots()
ax1.plot(t, data,'k-', linewidth=1)
ax1.grid()
ax1.set_xlabel('time (s)')
ax1.set_ylabel('signal amplitude')
fig1.set_size_inches(fig_size)
fig1.show()
fig1.savefig('speech_signal.png', dpi=200)

# Order of the autoregressive model
m = 10

# Factor for initialization of matrix P
delta = 100

# RLS with forgetting factor 1
lamb = 1
histo_weights1, error_weights1 = RLS_autoregressive(data, m, lamb, delta)

# RLS with forgetting factor 0.92
lamb = 0.95
histo_weights2, error_weights2 = RLS_autoregressive(data, m, lamb, delta)

# RLS with forgetting factor 0.89
lamb = 0.90
histo_weights3, error_weights3 = RLS_autoregressive(data, m, lamb, delta)

# Time axis for RLS weights
t_autoreg = t[:-m]

# Plot weight estimation evolution over time for forgetting factors 1 and 0.89
fig2, ax2 = plt.subplots(2)
ax2[0].plot(t_autoreg, histo_weights1,'-', linewidth=1.5)
ax2[0].grid()
ax2[0].set_ylabel('$\lambda = 1$')
ax2[1].plot(t_autoreg, histo_weights3,'-', linewidth=1.5)
ax2[1].grid()
ax2[1].set_ylabel('$\lambda = 0.90$')
ax2[1].set_xlabel('time (s)')
fig2.set_size_inches(fig_size)
fig2.show()
fig2.savefig('estimated_weights_evolution.png', dpi=200)


# Average error on time windows for cleaner plot
win_leng = 50
error_weights = zeros((3,len(error_weights1)))
error_weights[0,:] = error_weights1
error_weights[1,:] = error_weights2
error_weights[2,:] = error_weights3

error_weights_average = error_weights[:,::win_leng]
for k in range(1,win_leng):
    error_weights_average = error_weights_average + error_weights[:,k::win_leng]
error_weights_average = error_weights_average/win_leng

# Plot error evolution over time
fig3, ax3 = plt.subplots()
ax3.plot(t_autoreg[::win_leng], log10(error_weights_average[0,:]),'r-', linewidth=1.5, label='$\lambda = 1$')
ax3.plot(t_autoreg[::win_leng], log10(error_weights_average[1,:]),'b-', linewidth=1.5, label='$\lambda = 0.95$')
ax3.plot(t_autoreg[::win_leng], log10(error_weights_average[2,:]),'g-', linewidth=1.5,
label='$\lambda = 0.90$')
ax3.grid()
ax3.set_xlabel('time (s)')
ax3.set_ylabel('$\log_{10}($error$)$')
ax3.legend()
fig3.set_size_inches(fig_size)
fig3.show()
fig3.savefig('autoregressive_error.png', dpi=200)