Make sure you’ve read everything in the Intermission module before proceeding.
Foundation concepts you need for this module
Revise the foundation material on mathematics from “Variables and indexes” up to “Functions”, and the recommended reading “Vectors and their uses” before watching the videos.
Module content
This is the start of the Automatic Speech Recognition material. In this module, we will make a preliminary attempt to extract salient features from speech signals, then use pattern matching to compare an unlabelled sample of speech to several stored samples with known labels (“exemplars”). Start with this very simple post, which defines the problem as one of extracting a feature vector (here with just 2 dimensions) and then doing pattern matching by measuring distance in feature space to exemplars: A super-simple speech recogniser
But that won’t handle anything other than stationary speech sounds. We need a way to deal with words and sequences of words. The problem is non-trivial because it involves sequences of variable length. This is recurring theme in Speech Processing, and in Natural Language Processing more generally.
Now work through the videos and readings which introduces a simple approach to pattern matching for variable length sequences: Dynamic Time Warping.
Here’s what you’re going to learn in this module:
Lecture Slides
Thursday lecture slides (google) [updated: 4 Nov 2025]
Total video to watch in this section: 52 minutes (divided into 5 parts for easier viewing)
Here are the slides for these videos.
Assumptions & references made in these videos
“You already know that the waveform is not the right representation for pattern recognition” arises from material in the early modules of the course where, for example, we found that only the magnitude spectrum is required to distinguish speech sounds, whereas phase can be discarded.
“You have a reasonable idea of this concept of a feature vector” simply means that you already know why we need short-term analysis for speech signals (because their properties change over time) and that each analysis frame can be characterised by a set features, such as its magnitude spectrum. The videos in this modules build on this, and develop a better feature vector than the magnitude spectrum.
“In the previous videos, we suggested that linear time warping would be one possible solution.” There is no previous video now, so linear time warping will be mentioned in the lecture as a precursor to Dynamic Time Warping.
Whilst the video is playing, click on a line in the transcript to play the video from that point. This is the start of the speech recognition part of the course and we're on a journey towards a model called the hidden Markov model, the HMM. But we're going to get there in some small steps. We're going to start with something much simpler than a model, something called pattern matching. That's in this module. We'll then move on to thinking about the features that we extract from speech a bit more deeply and we'll do some feature engineering and then finally we'll get to the hidden Markov model which is a probabilistic generative model. Along the way we're going to learn an algorithm called dynamic programming and we're going to see that now in the form of dynamic time warping. We'll see that when we move from pattern matching to a probabilistic model we might have specific requirements for the features that we extract from speech so there's some interaction between the model we choose and the features that we choose. That's why we need to do feature engineering. And then we'll see dynamic programming again for the hidden Markov model in the form of the Viterbi algorithm. You should already know that the waveform is not the right representation for pattern recognition. For example it includes phase information which we know isn't particularly useful for discriminating for example one phone from another. You should have a reasonable idea of this concept of a feature vector but we're going to develop that a little bit more in this video. So we're going to throw away the waveform and replace it with a sequence of feature vectors and we're going to use a sequence of feature vectors for pattern matching using a very simple method with whole word templates. But even this very very simple and outdated method that we're going to talk about now already has to tackle the fundamental problem of dealing with sequences of different lengths. So you know the waveform is not a good representation because it combines source and filter and it includes phase. We only want the filter. So we want something like the spectral envelope. We know that speech waveforms change over time and that we can use short-term analysis to deal with that. And we're going to extract a feature vector from each frame in a short-term analysis of speech waveform. And to deal with this problem of sequences of different lengths we're going to need an algorithm and the core of the algorithm is going to be about finding an alignment between two sequences. In the previous videos we suggested that linear time warping would be one possible solution. Clearly that's too naive. Speech doesn't behave like that. And so now we're going to develop the real algorithm which is going to be a dynamic or nonlinear time warping to align two sequences of differing lengths. So here's what we're going to learn and it divides into two parts. In the first part we're going to extract features from frames of speech and the destination of that first part is a sequence of feature vectors to replace the waveform. And that will be our representation of speech for doing pattern matching. In the second part we'll take that sequence of feature vectors and we'll match one sequence that we know the label for against another sequence that we're trying to put a label on. In other words we're trying to do speech recognition and we'll find the distance between those two sequences of feature vectors. Let's start on the first part. And in the beginning of this course when we talked about signal processing and how speech was produced we took some inspiration from speech production. And that led us to a source filter model which we could use to explain speech signals and could be the basis for speech synthesis and all sorts of other things. That's still relevant but also relevant would be some understanding of speech perception. So let's start with that because we're going to take some inspiration from human speech perception to do feature extraction for automatic speech recognition. The cochlea is part of our hearing system. On this diagram it's this part here. And the cochlea acts like a bank of filters that respond to different bands of frequencies in this incoming audio. Of course sounds coming into the ear aren't all speech but we're only interested in speech here. So sound comes into the ear and it ends up in the cochlea. In our bodies the cochlea is coiled into a spiral but that's just to save space because we need to leave more room in our head for our brain. And so to understand it we don't need to worry about it being a spiral we can just draw it out as a straight structure like this. So sound comes into the ear as a sound wave and it propagates down the cochlea and different places along the cochlea, so this is place, respond to different frequencies in that incoming sound wave. They're effectively little resonating filters, each of them responding to a narrow band of frequencies. At some parts in the cochlea those little resonating filters respond to high frequencies and at the other end of the cochlea they respond to low frequencies. So we can understand the cochlea, which is the device in our bodies that converts sound pressure waves into nerve signals to be sent off for interpretation by the brain, as a bank of filters. The cochlea spreads frequency along its length, along its space. One of the most important features of this bank of filters in the cochlea is that it's not spaced linearly on a Hertz scale. It's spaced quite non-linearly. There's lots of mathematical functions that are used to approximate that non-linearity. The most common one in speech technology is called the Mell scale and the Mell scale looks like this. As we linearly increase in Hertz the Mell scale is compressive, it curves this way. So for example the difference in Hertz here, these high frequencies, is a relatively small difference in Mell. But down here the same difference in Hertz is a much larger difference in Mell. So it's this compressive nature to this curve that's important. What that means is that human hearing is less sensitive to frequency differences up here in the high frequency range than in the low frequency range. I'm going to use that when we extract features from speech signals for speech recognition. We're going to extract more or denser features in this sort of frequency range, which happens also to be where all the information in speech is, and fewer coarser features up in the higher frequency range. So the cochlea is like a bank of filters and they're spaced non-linearly in Hertz or linearly on some perceptual scales such as the Mell scale. And we can use those along with our knowledge of the spectral envelope to start extracting some useful features for automatic speech recognition. So let's see how a Mell scaled filter bank can be used to extract the spectral envelope from a speech signal. So here when I say auditory system I really just mean the peripheral part, the cochlea, the bit that's converting sound waves into nerve impulses. We're not talking about any processing in the brain. Let's remember what a bandpass filter is. It's something that has a lower frequency limit and an upper frequency limit and it extracts those frequencies from the input signal. And it's easiest to draw in the frequency domain where it rejects everything outside of its band and it accepts everything inside its band. So that will be an idealized perfect rectangular bandpass filter drawn in the frequency domain. The cochlea is like a set of such filters, a bank of filters, and they are spaced along a Mell scale so they get further and further apart in Hertz at higher frequencies and their widths also get wider. So a simplified idea of what the cochlea does will be that there are bandpass filters like that, that's one of them, and then another one a little wider, and then another one wider still, and so on. I've just simplified the cochlea as only having four bands, of course it's got many more than that, but we can see that the center frequencies of these filters get further and further apart as we go up in higher frequencies and that the widths of these filters also get wider, the bandwidths. These are bandpass filters, they pass through a band of frequencies. Now these idealized rectangular bandpass filters aren't actually possible physically, so the cochlea isn't like that. The cochlea has a set of overlapping filters that have some more realistic shape and a slightly better approximation than a bank of rectangular filters would be a bank of triangle filters that overlap. So we might have filters that look like this, getting wider and wider and further and further apart, and this spacing here is on a mel scale. Again I've just drawn four filters, we're going to use more than that to simulate what the cochlea does. So we've got a simplified model of the cochlea as a sequence of triangular filters, a bank of triangular filters, and we're going to use that now to extract features from speech waveforms.
Whilst the video is playing, click on a line in the transcript to play the video from that point. So what do we mean by a feature? Well, one possible set of features for speech recognition would be the raw waveform samples themselves, but we've already dismissed that as a representation that's not particularly useful. It includes phase and other things that we're not interested in. An alternative would be what we're seeing here, which is to discard phase and to just look at the magnitude spectrum. That's already better than the waveform because it's invariant to phase. So remember what we're seeing on this picture. This is the output of the discrete Fourier transform. There are bins here up to the Nyquist frequency, and that set of numbers could be a set of features for doing automatic speech recognition. So the raw DFT features, but just the magnitudes and not the phases. But we also know that this includes information that we're not interested in in general for doing speech recognition. We can see here that we can resolve all the harmonics of F0. And in general, F0 is not a useful feature for speech recognition. What we would like would be something that's this envelope. So an even more useful feature than a DFT bin would be the output of one of this bank of filters. So let's just draw one of the filters. It rejects everything, then it passes its band with some response shape. Here it's triangular. What is the output of this filter? Well, if we put the speech signal through this filter, it would pass through all of these frequencies and reject everything else. And what we're interested in is summing all of the energy in that band, summarizing that as a single number for a particular analysis frame. So it's the amount of energy in this frequency range in that analysis frame. And that's what we mean by the output of a filter. So each filter's output will be a useful feature for doing automatic speech recognition, even more useful than the DFT bins themselves. So we've got a bank of filters, and each filter for each analysis frame summarizes the amount of energy in its pass band and writes that out as a number. And what we're going to do now is we're going to just store them into a vector. So here's a speech signal, of course it's in the time domain. It's going to always be easier to think about everything in the frequency domain. So let's draw the magnitude spectrum. And what's this is? This is the magnitude spectrum for some analysis frame. So we've taken some section of the speech signal, we've cut it out, we've probably applied a tapered window, and we've taken the DFT to get the magnitude spectrum. So that's frequency. We're going to extract from this magnitude spectrum a set of features. The features are going to be the amount of energy falling inside each of the pass bands of a bank of overlapping triangular filters of some number of filters that we have to choose, but it's going to be something like 20 to 30 typically. And we're then going to store each of those in a vector. The first one is going to produce some output, and we'll store that in the first element of the vector. And then the next filter will summarize its energy into some output. We'll store that in the second element of the vector, and so on. So this vector stores the features for a single analysis frame, for this analysis frame here. Now speech changes its properties over time, and so we analyze it with a sequence of such analysis frames. So let's just talk a little more generally about this idea of sequences, because it's going to be the core problem we have to solve eventually. Sequences are everywhere in language. A waveform is a sequence of samples. We can analyze that sequence of samples with a sequence of overlapping analysis frames, and there's some correspondence between a frame and the samples that fall inside it. Back in speech synthesis, we could think about a sentence being a sequence of words, a spoken word being a sequence of phones, or a written word being a sequence of letters. Now we've gone from a waveform being a sequence of samples to being a sequence of overlapping analysis frames, and each frame giving rise to a feature vector that's extracted from it. So a waveform has become a sequence of feature vectors. Now what do all these sequences have in common? Well, they're all of variable length. There's some alignment between sequences at one level and sequences at another. For example, phones align with words, but it's not trivial because the number of phones per word is variable. Or we might also want to align sequences of the same type. If we were building a spelling correction system, we might need to align two letter sequences, the sequence of letters somebody typed and the sequence of letters of all the words in our dictionary, and compute something called the minimum edit distance or the Levenstein distance to find the word that's closest to what they typed and correct it. Or we might have the output of a speech recognition system, which gets some words right and some words wrong, and we'd like to compute its accuracy or its word error rate. That's something we'll need to do later in the course, and that will involve aligning two word sequences, which are potentially of different length and have a non-trivial alignment. But what we're going to do here and now is we're going to align two speech signals represented by sequences of feature vectors, and we're going to measure how similar they are. We're going to use that to do pattern matching and therefore to label unlabeled sequences with the label of the closest stored pattern that we already have. Now in spoken language processing, most of the time, the alignments between these sequences are monotonic. That is, as we advance through, for example, a sequence of words, we also advance through the sequence of phones that correspond to that sequence of words. In a left to right fashion, we don't backtrack, there's no reordering. But the alignment isn't linear. We don't just move proportionally to the two sequences. It's nonlinear, it's dynamic. For example, the number of phones in a word varies word by word. And this dynamic alignment is also going to be true when we align two sequences of feature vectors. So let's get back on track after talking rather abstractly about sequences. What we really care about then here are sequences of feature vectors. So let's see how that works. We've already seen that a single analysis frame, we extract its magnitude spectrum, we put a bank of triangular filters on it, and we write the outputs of those filters into our feature vector and store them. And that gives us one feature vector for one analysis frame. And then we'll just move the analysis frame forward and repeat and move it forward and repeat in the usual way. I'm going to start drawing my vectors vertically because I'm going to have a sequence of them and it will become clearer. And so we'll have a sequence of analysis frames giving rise to a sequence of feature vectors. This analysis frame, we go to the magnitude spectrum and then we do some filter bank analysis, give it a feature vector, next one, next one, and so on. This is the first step in almost every automatic speech recognition system that's ever been built is to get away from the waveform and extract from it salient features, which are at a frame rate. So they're every, for example, 10 milliseconds instead of 16,000 times per second in the time domain. So much lower rate in time. So there's far fewer of them and that they have a relatively low dimension here. So we've done the first and most important step in every automatic speech recognition system, get rid of the inconvenient waveform, distill it down into a sequence of feature vectors. And that is the representation we're going to work with from now on.
Whilst the video is playing, click on a line in the transcript to play the video from that point. So in this module, we're not going to develop any fancy probabilistic model for doing that. We're going to do something very, very simple. We're going to do pattern matching, but we're going to do this old-fashioned method for a very good reason, because it helps us understand the dynamic programming algorithm, which we're going to need again when we do do Hidden Markov Models. So if I take a sequence of feature vectors for a recorded, say, spoken word that I knew the label for, I'm going to call that an exemplar, a stored example with a label. It looks like this. I've got somebody to record the word three. I've got the waveform, and from that, I've extracted the feature vectors. And that is what I'm going to call an exemplar. If I want to do speech recognition for just the digits, I'll store one exemplar for each digit that I want to recognize. So one, two, three, and so on. I'll store them, and all I will store are their sequences of feature vectors. So that's an exemplar of the word three, and we can throw away the waveform. Going to restrict everything to whole words in isolation, so we're just going to build an isolated word recognizer. That sounds a bit restrictive, but it won't be. It'll actually be very easy to generalize that, but we won't do that generalization until we get to Hidden Markov Models. For the purposes of dynamic time warping, we'll completely restrict ourselves to isolated whole words, and let's just assume we're doing digits to keep things simple. So I've stored an exemplar of every word that is in my vocabulary, and I'm going to have an incoming speech signal from which I'll extract a sequence of feature vectors, and I would like to put a label on that. I would like to do automatic speech recognition to it. I'm going to do that by measuring the distance between that incoming unlabeled sample and all of my stored exemplars. So we need to think about how you measure the distance between two sequences of feature vectors. Here's my exemplar, and I always get rid of the waveform. So an exemplar is a label and a sequence of feature vectors stored, and that's stored inside my speech recognizer, and I'm going to measure the distance between that and some unknown incoming speech. So of course I do the same feature extraction for that and throw away its waveform, and now I want to measure the distance between these two things. The distance is going to be the dissimilarity. So a big distance means they're very unlike each other. They're unlikely to be repetitions of the same word. If there's a small distance, that's a small dissimilarity or a high similarity, which means they're more likely to be repetitions of the same word. I'm going to use this distance to decide for some incoming unlabeled sample of speech which of all my exemplars is closest, which has the smallest distance, and I'm going to put that label onto this unknown incoming speech. So how could we measure the distance between these two sequences? The simplest way I can think of is to define it as a sum of local distances. I will measure how similar the start of this thing, the exemplar on the top, sometimes we call that a template, is with the start of the unknown, and I'll measure how far apart the middles are and how far apart the ends are. So what I'm going to do is I'm going to measure local distances, and I'm going to define the total or global distance between the sequences as the sum of all of those local differences or distances. So you'll hear me using terms distance and difference, that's the same thing, and this global distance between this exemplar and this unknown is just equal to the sum of these things. Now how did I know that those particular local distances are the right ones to add up? Well, I don't yet know that. What we need to do is form some correspondence, which I'm going to call an alignment between the sequence of feature vectors for the exemplar and the sequence for the unknown, and there are many possible alignments. So the hard problem isn't one of computing distance, because I'm just going to keep a very simple local distance for now, I'm going to use the Euclidean distance between the two vectors, so a simple geometric distance, that's easy. The hard problem is exploring all of these possible alignments and deciding which one is the one to use to compute the global distance as the sum of local distances. So which local distances are the right ones to add up? We had earlier proposed that we'll simply make this as linear as possible, so we'll just stretch in some uniform way, perhaps like that, and always use that. That's clearly too naive, because speech isn't like that. We want to do something better than that, we want to dynamically stretch the sequences, and what we're going to do is we're going to make sure that the most similar parts in the exemplar are compared to their most similar parts in the unknown, and compare like with like. It's the only obvious thing to do, I think. So the hard problem is finding the alignment from amongst the many, many possible alignments. Let's just check how my diagrams relate to the way that they're drawn in Holmes and Holmes. So that you can relate this to the reading. This is a picture from Holmes and Holmes, and this is a bit like a spectrogram, but it's actually a sequence of feature vectors, and they're very similar to each other. In this diagram, this is time in frames, and this is the index of the feature vector, which is the same as which filter it is in the filter bank, and that's on a frequency scale, ascending in frequency. So each column of this picture is one of my feature vectors, and so this picture from Holmes and Holmes corresponds exactly to my picture. That's how Holmes and Holmes are drawing it, that's how I'm drawing it. I'm drawing the vectors out explicitly. So we'll use this diagram from now on.
Whilst the video is playing, click on a line in the transcript to play the video from that point. So we've talked about storing exemplars, which are just sequences of feature vectors extracted from known recordings with labels, and the idea of computing a distance to the unknown, the thing we're trying to recognize. And we've realized that the only hard part of that distance computation is aligning these two variable length sequences with each other so that we know which local distances to add up. So we're going to have to find some efficient way of considering, exploring every possible way of aligning the two sequences, but we should do that in the most efficient way possible because there might be a very large number of such alignments. And there's going to be a beautiful and elegant algorithm to do that called dynamic programming. Dynamic programming is the general term for this whole family of algorithms that have the same property. As it's applied here, because these are two time sequences, we're going to be warping them in a nonlinear way, and so this is known as dynamic time warping. So I used the term pattern matching earlier, let's just be clear what those words mean. These are the patterns, and the matching is trying to find the global distance between these two patterns. And the global distance will be the one that gives the most favorable alignment of the two frames and then sums up all the local distances. We're going to develop a dynamic programming algorithm, a special form called dynamic time warping. So how do you develop an algorithm? That sounds pretty hard. It's usually easiest to start when you're developing an algorithm with some step that's actually in the middle of the algorithm, once it's running, the standard iterative step that runs again and again, the one that repeats over and over, and understand that step. And then after that, deal with the special cases of starting and ending. Let's do that then. We're aligning these two sequences, there are many, many possible alignments, but we've got partway through the alignment, and we're doing the following. We're working our way through the template one at a time, and we're working through the unknown and finding the alignment between the two. And I keep this example simple, I'm going to assume my algorithm works through the template one frame at a time. So each iteration of my algorithm will go one step through the template, and it will decide whether to make a step through the unknown or not, so that the two eventually align. That's not fully general, but we'll generalize that in a moment. So let's imagine I've got to a state in my algorithm, where I've decided to consider comparing or aligning the fourth frame in the template with the third frame in the unknown. So I'm going to say, what about the cases where these two align? One way of doing that, one way of arriving at that point in the alignment, would be to align like this, and I could write that out as follows. The first number is the frame in the template, so frames are just counting through in time, like this, 1 to 7, and the second number is the frame in the unknown, counting through the unknown. So I've done 1 1, 2 2, 3 3, 4 3. Now let's think about all the other alignments between these two sequences, that also align 4 and 3. There's this one, and there's this one. Remember, I'm making this simple assumption that I'm just counting 1 to 7 through the template, and I decide how fast to move through the unknown. So I'm dynamically moving through the unknown, as I just advance through the template, in this simplifying case. These are the only possible alignments, given those constraints. See that I have not yet done the computation for what happens next. There are three possible alignments all arrive in this state here, of aligning 4 with 3. One of these three will be the best alignment. What's the best alignment? Well, it's the one that sums up local distances. For example, sums up this one, this one, this one, and this one, that would be the first case. And the local distance is just the Euclidean distance between the corresponding frames. Or I might consider this one, and I'd sum up those four local distances, to find the distance so far of getting to here. Or I might consider this one, and I'd sum up those four local distances, and get the distance so far of getting to here. In general, these three things will have a different distance so far of arriving at this point in the alignment. One of them will be the smallest, and that'll be the best one. And here comes the dynamic programming insight. Without going any further in the alignment, without doing any of the remaining computation that I need to get to align the two ends of the sequence, I can already choose which of these is best. Let's imagine that one's the best. And I can abandon these two. Why is that? It's because whatever happens after this point here is completely independent of what happened beforehand. Let's write that out. If I assume that these two align, then how the frames before it align, and how the frames after it align, have no interaction, they're independent of each other. The best alignment to the left, and the best alignment to the right, can be computed independently. In other words, if I was to do the dumb thing of computing all three of these all the way through to the end, so I kept on going and computed them, and there's, of course, many possibilities here, there's more than one way of going forward, so there's lots of computation still to do, the best way of aligning the remaining frames has got nothing to do with how I align so far. In other words, the computation required here is identical to it required here and here, because they're all starting from the same place. So I'd be wasting my time. If I know that this gives the lowest distance so far, summed across those four local distances, there's no point wasting my time doing this computation, because it's just the same computation as I'm going to do here anyway, and since we know they're not winning at this point, they're not going to win by the end either. That's the insight of dynamic programming, that we can save computation because we see that there's repeated computation, there's repeated subparts to the problem. Let's just state that again, because this is the key, key point of dynamic programming. The state of the algorithm at which frame 4 and frame 3 align makes everything to the left independent of everything to the right, and we can cut the problem into two parts, two halves, and we can compute this part and this part quite separately. We don't need to repeat all the possible combinations. That's conceptually quite hard, and you probably won't get it first time, but we're going to go through that through the full algorithm in a moment, and then we're going to come back to it and do it all again with Hidden Markov Models. So what I really want you to be able to do is to understand dynamic time warping as a form of dynamic programming, as an algorithm, and that's already getting harder than average for this course. Being able to see that dynamic programming can be applied to a particular problem, such as sequence alignment, is really quite difficult. If I presented you with a new problem and said, could you apply dynamic programming to this, that'd be really quite difficult, but I'd like you to be able to get to that point that you can see it is applicable to two sequences of feature vectors, and later it will be applicable for Hidden Markov Models, and what's super hard, and I don't expect you to be able to do, is to actually devise the data structure required for doing dynamic programming. And I'm just going to present the solutions to that, first for dynamic time warping, and later for Hidden Markov Models. So we'll never ask you in this course to devise a new data structure and write out a new dynamic programming algorithm for some new problem. We just really want you to see that dynamic programming works as an algorithm, what the core insight is, that we can make the past independent of the future by knowing the present, being able to see that dynamic programming is applicable, but not going that extra step of actually deriving it for a new problem. We're now going to spell out dynamic time warping as an algorithm, and the algorithm is going to need a data structure in which it stores the information as it proceeds, and that data structure is going to be very simple in fact, it's actually going to be something we'll call a grid. Here's the problem. We're exploring all possible alignments between these two sequences, a template or an exemplar with 7 frames, an unknown with 5 frames, and we'd like to consider all possible ways of aligning each of the 7 frames with each of the 5 frames. So the core operation in that is considering a pairing of frames, an alignment. For example here, the 6th frame of the template aligns with the 3rd frame of the unknown. That's the key element, the little building block of the algorithm, is this alignment between a frame in one sequence and a frame in the other sequence. So how many possible such pairings are there? We've got 7 things, each of which can align with 5 things, how many possible pairs? You could pick one thing from here and one thing from here and pair them up. How many such pairs are there? Well they're just 7 times 5. Each of the 7 things could align with each of the 5 things. So our data structure is going to need 7 times 5 elements. The obvious thing then would be to make a grid that's 7 by 5. That's what we'll do. So I'm going to draw out the template on the vertical axis. Remember this is a sequence of feature vectors, so that's time and that's frequency moving up through the filters in the filter bank. So you could think of those as little spectrograms if you prefer. Just space them out a bit because I want to draw my grid as big as possible. So that's the grid, that's the data structure that the algorithm is going to use. We've got 2 sequences of feature vectors, one that has a label and one that we're considering whether to give it the same label if it's sufficiently similar, if its distance is small enough. And we're trying to compute that distance as a sum of local distances and we want the smallest possible sum of local distances, the most optimistic one, because this is the only sensible thing to do. So we've got global distances and local distances going on in this grid. Let's just spell that out. What's a local distance? Well, at some cell which represents aligning this feature vector with this feature vector, there is some local distance we could compute. We could take these 2 vectors and we could compute the Euclidean distance between them and that would be the local distance of that cell in the grid. We've also got the global distance, which is a sum of local distances, for a particular alignment of the 2 frames. So let's just draw that out for one particular alignment. Imagine that we always align the first and the first frame, the last and the last frame. An alignment would be a path through the grid. It might look like this. We'll align those 2 and take their local distance and then we'll move here and we'll add the local distance between these 2. And then we might go here, here, here, here and here. And the global distance of this path, of this way of aligning the 2 sequences, is this local distance plus this one, plus this one, plus this one, plus this one, plus this one, plus this one. What we're trying to do is to explore every possible way of getting from here to here and find the one that adds up local distances to make the smallest possible global distance. There are many, many paths through this grid. I'm not going to draw them all out because we don't have time, but let's start drawing a few to give you the idea. There could be really, really stupid ones like this. That's really unlikely, that the first frame of the unknown aligns with all of the frames of the template and then we stay at the end of the template and align that last frame with all the remaining frames of the unknown. Or we could go like this, or we could go like this, or we could go like this, and so on. You get the point. All the way to the one that does this. A very large number of those. We're trying to do that in an efficient way.
Whilst the video is playing, click on a line in the transcript to play the video from that point. Let's just relate that back to the picture we had before when we drew the two sequences out on top of each other. That was one of the possible sequences, and that would be like moving through the grid in this fashion. And so the distance so far of arriving at this point, and assuming that this is an alignment, would be this local distance plus this local distance plus this local distance plus this local distance. And what we'll store in this cell of the grid would be the distance so far of coming from the beginning to that cell in the grid. So let's develop the full algorithm. So I'm going to generalize now, and I'm going to say that as we move through the two sequences, we have to move monotonically. In other words, as we've advanced through the frames in this one, we can't go back and consider a previous frame, because speech certainly never does that, it's monotonic. But we can advance through one frame here, or one frame here, or one in each at the same time. What does that mean on the grid? Well, let's always assume that the first two align. It means that the allowable moves in the grid are to go this way, this way, or this way at any time. That's all we're going to allow. So let's write the rules of the game. We have to start here at 1, 1. We have to get here at 7, 5, and we want to find the lowest distance path between those two points. And that's just the sum of local distances. We've got to visit grid cells along the way, and every time we visit a cell, for example, if we visit this cell, we have to accumulate the local distance between this frame and this frame. If we step on that cell, it costs us that amount of local distance. We've got to get from here to here, stepping on the cells as we go, accumulating local distances, and try and get there by accumulating the smallest amount of total distance or global distance along the way. Draw those two in because we're always going to align first with the first and last with the last. And I just showed you that we could enumerate every single possible path from the start to the end. Compute its global distance and write it down. After having done all of them, pick the one with the lowest distance. That would work. You'll get the right answer. But there'd be a lot of repeated computation. Those paths would have an awful lot in common. Many, many sub-sequences or sub-paths would be the same, and that's wasting computation. So dynamic programming gets us around that. It avoids repeating any computation. Everything is computed just once, and that computation is shared between all the different paths that require that bit of computation. Let's just do the algorithm from the beginning, see how that works. So I'm going to start here. And the distance so far is the local distance between this and this. So that's the partial path cost or distance. We often see the word cost used in the literature. So cost and distance are the same thing. A high cost means a high distance. It's a bad thing. So we've accumulated some distance. So the partial distance at this cell is just the local distance because we've only accumulated one. Now what can I do in my algorithm? Well, I said the allowable moves were to go this way. So I could move here, and I would accumulate a second local distance between this frame and this frame. And I would add that to what I'd already had from the previous cell, and that would be my partial distance so far. So I could write into this cell that distance, which is just going to be this local distance plus this local distance. That's it. I could do the same for the other moves. I'm allowed to go here, and I would accumulate the local distance between this pair of frames and add it in. And I'd write into that cell that partial path distance. That's the cost or the distance of getting to that cell from the beginning. And finally, I would do this one, get a local distance and write that in there. So far so good. That's really very simple. Now things get interesting in the next step of the algorithm. Let's imagine extending this path this way. What's the partial total distance now? Well, it's that local distance plus that local distance, which we'd already written into the cell. So it's whatever was in this cell plus this local distance. And I write that into that cell. But there's another way of getting to the same cell. I could start here with the accumulated distance so far and move here. I've now got two competing paths arriving in this cell, and one of them will be cheaper than the other. You'll have a lower distance than the other. Dynamic programming tells me I just take the lowest of those and write that in the cell. So if I computed this purple one first and wrote it in the cell, and then I computed the green one and I could get there more cheaply, then I would just overwrite that value with the lower value. I'd take the minimum of all the values. So I take the cheapest way of getting to that cell and just write that value into the cell. And that's the core step of dynamic programming. How has that saved us computation? Let's consider starting from this cell and finding the best path to the end, which is going to involve repeating that operation over and over again. Well, we do not have to take the purple path and find it to the end, and take the green path and find it to the end, because one of them's already won at this cell. So we only have to pick the winner and propagate that one forward, and we're effectively abandoning the other one. So the core operation in dynamic time warping happens at a cell that we can arrive at from multiple directions, and the global distance so far that we write into that cell is just equal to the minimum of these plus the local distance here. And that's it. And we just repeat this operation for every single cell in the grid until we do it for this cell, which of course we could arrive at in this direction, this direction, and this direction. So we take the minimum of these three things, add the last local distance in, and then in that cell is a number, let's just call it big D for global distance, and that number is the cheapest way of getting from the beginning to the end, accumulating the smallest amount of local distance on the way. So that's the core operation, and all you need to do is just visit the cells of the grid in an appropriate order, such that all the computation you need has been done by the time you get there. And that would be very easily achieved by, for example, visiting them row by row in this order, that would work, or visiting them in this order, that would work, or any other order that you can think of, such that whenever you get to a cell, you've previously visited all the cells here before you get there, so that the partial distances are available for you to take the min of and to write it into this cell plus that local distance. So the amount of computation in this algorithm is to do with the length of this sequence times the length of this sequence. So there are 35 operations to be done to compute this total distance. You've just got to visit each cell once, compare that to enumerating all the paths out one by one. That would involve this much computation, and don't worry, I'm not going to write them all out because we'll be here for hours. It would involve adding this to this, to this, to this, to this, to this, to this, to this, to this, to this, to this. So that's already 11 computations to do that one path. And then we'd forget that we'd done that, and we do one, two, three, four, five, six, seven, eight, nine, nine more computations, one, two, three, four, five, six, seven, eight, nine, ten, another nine computations, et cetera, et cetera, for all of the paths until you get to one, two, three, four, five, six, seven, eight, nine, ten, another plus 11 here. So you add all of those together, and you don't have to enumerate them out to see that that's much, much bigger than 35. It's much, much bigger because we're visiting the same cells over and over again. Dynamic programming avoids that. It visits each cell once, it does each computation once. And that's the key idea, sharing computation between all the subparts of the problem. And that was allowed because knowing that we visit this cell in an alignment makes everything before then independent of what happens going forwards. In other words, it doesn't matter how you got here, you could have come from any of these directions, you don't need to remember that because it doesn't change how you get to the end. There's no dependency between them. So the future is independent of the past given the state or the current cell. So we might state that as saying the past and the future become independent when we know the present. The present is everything we need to know. There's no memory in the system. This is conceptually a little hard, but we'll see it again with Hidden Markov Models. And the key of the Hidden Markov Model will be to make sure it has no memory so that the past and the future become independent given the present. And then we get to do dynamic programming. So it'll turn out that we'll invent a model specifically so that we can do dynamic programming with it. So we start with the idea of the distance between two sequences of feature vectors and realize that the only difficult part of that is deciding how to align them with each other so we know which local distances to add up, to make a global distance, to announce as the distance between the two sequences. And we just repeat that dynamic programming for each of the templates, each of the exemplars we have. One of them will have the lowest global distance and that will be announced as the label of the incoming word we're recognizing. We develop the algorithm and in the heart of the algorithm really is a very simple operation of taking the minimum and we had a very simple data structure in which to store the results of that operation, a grid. I'm going to go off and do some feature engineering in the next module and then come back to this idea and realize that dynamic time warping has many flaws. That's why it's no longer used for speech recognition, but we're going to fix them all. I'm going to fix all of that with a Hidden Markov Model. We're going to understand what this word hidden means. It's its state sequence that's hidden. That's another hard concept to come, but we'll find that the data structure that we might use to do dynamic program with a Hidden Markov Model is something quite like a grid. We're going to call it something different. We're going to call it a lattice, something more general than a grid. And on that data structure, we'll also be able to do almost exactly what we just did with dynamic time warping, the same sort of dynamic programming. It's going to get given a different name because it's applied to a different model. It's going to be called the Viterbi algorithm. So to summarize that, then dynamic time warping, and in particular, this local distance measure, which we didn't say very much about today, this Euclidean distance is poor. It doesn't account for variability. So it doesn't have any idea of natural variance in speech. So we better fix that. And we're going to replace it with a probabilistic model, which will be learned from multiple exemplars, multiple ones to capture the amount of natural variation. So we won't learn a model from a single recording of someone saying three, we'll get them to say three lots of times, or get lots of people to say three lots of times, and we'll form a probability distribution of the feature vectors for that word. That variability will be captured using Gaussian probability density functions. The Gaussian has not just a mean, but also a variance to capture variability, but also potentially something called covariance, which is how one element of a feature vector covaries with the other elements. And that covariance is going to turn out to be a rather awkward thing. We'd like to pretend there was no covariance. So we're actually going to do some pretty heavy duty processing to our feature vectors to try and make sure that isn't any covariance between the elements of the vector. In other words, that they become statistically independent of each other, they do not covary. So we're going to do feature engineering, motivated by our desire to use Gaussian probability density functions, and our very strong desire not to have to model covariance. So we're going to try and remove covariance. And the filter bank features are going to exhibit a lot of covariance, and are not going to be suitable. We're going to do some further steps on them. So in fact, we're going to do that feature engineering first, in the next module. We'll develop these features, which for very many years were the state of the art in automatic speech recognition, called Mel Frequency Cepstral Coefficients. We see mel still in there. The mel filter bank is going to be the first stage in extracting those features, but we're going to go further to remove covariance. When we've got those features, they will be suitable for modeling with Gaussians that can model the mean and the variance of each element of the feature vector, but assume there's no covariance between the elements. And that will then be the core of our Hidden Markov Model. We'll put those Gaussian probability density functions into the states of a finite state machine, and that will be the Hidden Markov Model.
Reading
Jurafsky & Martin – Chapter 9 introduction
The difficulty of ASR depends on factors including vocabulary size, within- and across-speaker variability (including speaking style), and channel and environmental noise.
Jurafsky & Martin – Section 9.1 – Speech Recognition Architecture
Most modern methods of ASR can be described as a combination of two models: the acoustic model, and the language model. They are combined simply by multiplying probabilities.
Holmes & Holmes – Chapter 8 – Template matching and dynamic time warping
Read up to the end of 8.5 carefully. Try to read 8.6 as part of Module 7, but rest assured we will go over the concept of dynamic programming again in Module 9. We recommend you should skim 8.7 and 8.8 because the same general concepts carry forward into Hidden Markov Models (again, we'll come back to this in Module 9). You don't need to read 8.9 onwards. Methods like DTW are rarely used now in state of the art systems, but are a good way to start understanding some core ideas.
Sharon Goldwater: Vectors and their uses
A nice, self-contained introduction to vectors and why they are a useful mathematical concept. You should consider this reading ESSENTIAL if you haven't studied vectors before (or it's been a while).
Sharon Goldwater: Basic probability theory
An essential primer on this topic. You should consider this reading ESSENTIAL if you haven't studied probability before or it's been a while. We're adding this the readings in Module 7 to give you some time to look at it before we really need it in Module 9 - mostly we need the concepts of conditional probability and conditional independence.
This is an ASR lab and is the start of the second assignment. You are going to build a very simple Automatic Speech Recognition system, starting right at the beginning with collecting/wrangling some data. You will then perform various experiments with it, and write up a lab report.
The instructions are here on speech.zone. Start by reading them all the way through.
Simon will give an overview/instructions to the class at the beginning of the lab session, so try to be there on time.
In the lab, you should try to run through the scripts used in the assignment, even if you don’t understand them all.
The hardest part of the assignment for most students is learning enough shell scripting to automate the experiments.
You can find some resources on using the shell and shell scripting in the Lab tab of the course Intermission page.
Private
- You do not have permission to view this forum.
This module introduced a conceptually hard algorithm: dynamic time warping. Sometimes, the only way to truly understand an algorithm is to implement it. The following exercise involves programming in Python, and so is beyond the scope of this course. But, if you can program, then you may find this quite helpful in developing your understanding of dynamic programming:
The material in this module on feature extraction was left incomplete. That’s because we don’t yet fully understand what properties we would like our feature vector to have. That will depend on what we are going to do with those feature vectors. The approach of measuring distance to exemplars in feature space is too simple because it fails to account for variability in at least two ways. First, all dimensions of the feature vector are treated as equally important, even though they almost certainly are not. Second, and more importantly, the single stored exemplar fails to capture the natural variation found in speech.
We will solve this problem by replacing the distance measure with a statistical model: the Guassian probability density function. This will make us rethink the feature vector and do some feature engineering to give it the right properties.
What you should know
- What is the basic problem we are trying to solve in Automatic Speech Recognition
- What is the ASR objective? i.e. get the most probable transcript given a specific audio recording
- How is this framed mathematically?
- How does this relate to Bayes’ rule?
- What’s does the the acoustic model P (O|W ) represent?
- What does the Language model P (W) represent?
- Cochlea, Mel-scale, Filterbank:
- Non-linear human perception of sounds: mel-scale, semitones
- What are filterbanks (more in module 8)
- Feature vectors, sequences, sequences of feature vectors
- How do we represent audio as a sequence of feature vectors?
- Exemplars, distances
- How do we measure the (Euclidean) distances between a pair of vectors
- local versus global distance between sequences
- What is a frame (i.e. vector) level alignment?
- Dynamic Time warping:
- How can you calculate the distance between two sequences of feature vectors of different lengths?
- What is the goal of Dynamic Time Warping? What are its inputs and outputs?
- Why is DTW a dynamic programming algorithm?
- Why is the alignment produced by DTW generally considered better than a linear alignment for measuring sequence similarity?
Key Terms
- transcription
- objective
- Bayes’ rule
- acoustic model
- language model
- log scale
- non-linear
- mel scale
- semitones
- cochlea
- filterbank
- feature vector
- sequence
- distance
- frame-level alignment
- dynamic time warping
- dynamic programming
- cost function

