Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
# Goodness of Pronunciation (GoP)
This code reflects the work described in the **[INTERSPEECH 2019](https://www.interspeech2019.org/)** published paper on **["An improved goodness of pronunciation (GoP) measure for pronunciation evaluation with DNN-HMM system considering HMM transition probabilities"](https://www.isca-speech.org/archive/Interspeech_2019/pdfs/2363.pdf)**.
This code reflects the work described in the **[INTERSPEECH 2019](https://www.interspeech2019.org/)** published paper on **["An improved goodness of pronunciation (GoP) measure for pronunciation evaluation with DNN-HMM system considering HMM transition probabilities"](https://www.isca-speech.org/archive/pdfs/interspeech_2019/sudhakara19_interspeech.pdf)**.

# Requirements :
* Python (tested with v.2.7.5 & v.3.5.7).
* Kaldi ASR toolkit (for documentation checkout : http://kaldi-asr.org/) considering acoustic models trained with _nnet2_ (Dan's recipe) (tested with nnet2 & nnet3).
* Kaldi ASR toolkit (for documentation checkout : http://kaldi-asr.org/) considering acoustic models trained with _nnet2_ (Dan's recipe) (tested with nnet2 & nnet3) on LibriSpeech.

# How to run the code :
Run the below code (**_prop_gop_eqn.py_**) to compute the score using the proposed GoP formulation by passing **_alignment_infile.txt_** and **_posterior_infile.ark_** generated for a given learner's utterance.
Run the below code (**_prop_gop_eqn.py_**) to compute the score using the proposed GoP formulation by passing **_alignment_infile.txt_** and **_posterior_infile.ark_** generated for a single learner's utterance.
```python
python prop_gop_eqn.py posterior_infile.ark alignment_infile.txt gop_outfile.txt
```
* The **_alignment_infile.txt_** file is the output of the forced-alignment of the learner's uttered speech (.wav file) and this is obtained using **_align.sh_**.
* The **_posterior_infile.ark_** file contains the frame level posterior-probabilities of the learner's uttered speech (.wav file) and this is obtained using **_nnet_am_compute.cc_**.
* The **_gop_outfile.txt_** file contains the score for each phoneme.

How to generate the input files:


**NOTE** :
* The above python script requires a lookup table to generate the scores for an acoustic model as discussed in the paper, which can be generated using the following code :
* The above Python script requires a lookup table to generate the scores for an acoustic model as discussed in the paper, which can be generated using the following code :
```shell
./gen_lookup_table.sh
```
Expand Down
116 changes: 65 additions & 51 deletions prop_gop_eqn.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,72 +9,86 @@
import pandas as pd
import numpy as np

path = os.getcwd();
path = os.getcwd();

# Modifying posterior.ark to posterior.txt
var1 = [path + '/reqd_files/' + sys.argv[1]];
subprocess.call(['bash', 'modify_post.sh', str(var1[0])]);

# Creating segment information list, aligned phones list & transition_id's list
var2 = [path + '/reqd_files/' + sys.argv[2]];
subprocess.call(['bash', 'extract_from_alignments.sh', str(var2[0])]);

#var1 = [path + '/reqd_files/'+ sys.argv[1]]

var1 = [sys.argv[1]]
subprocess.call(['bash', 'modify_post.sh', str(var1[0])])

# Creating segment information list, aligned phones list & transition_id's list
#var2 = [path +'/reqd_files/'+sys.argv[2]]
var2 = [sys.argv[2]]
subprocess.call(['bash', 'extract_from_alignments.sh', str(var2[0])])

with open(path + '/reqd_files/tmp_segments.txt','r') as f:
x = f.readlines();
number_of_segments = [int(tmp.split(' ')[0]) for tmp in x];
x = f.readlines()
number_of_segments = [int(tmp.split(' ')[0]) for tmp in x]
print(number_of_segments)
with open(path + '/reqd_files/tmp_t_ids.txt','r') as f:
x = f.readlines();
transition_id = [int(tmp.rstrip().split(' ')[0]) for tmp in x];
x = f.readlines()
transition_id = [int(tmp.rstrip().split(' ')[0]) for tmp in x]

with open(path + '/reqd_files/tmp_phones.txt','r') as f:
x = f.readlines();
aligned_phones = [tmp.rstrip().split(' ')[0] for tmp in x];
x = f.readlines()
aligned_phones = [tmp.rstrip().split(' ')[0] for tmp in x]

with open(path + '/reqd_files/posterior.txt','r') as f:
x = f.readlines();
posterior = [tmp.rstrip().split(' ') for tmp in x];
num_of_senones = len(posterior[0]);
total_num_frames = len(posterior);
x = f.readlines()
posterior = [tmp.rstrip().split(' ') for tmp in x]
num_of_senones = len(posterior[0])
total_num_frames = len(posterior)
#print(total_num_frames,num_of_senones)
with open(path + '/reqd_files/lookup_table.txt','r') as f:
x = f.readlines();
lookup_tab = [tmp.rstrip().split(' ') for tmp in x];

series = pd.Series(number_of_segments);
cum_number_of_segments_tmp = series.cumsum();
cum_number_of_segments=cum_number_of_segments_tmp.tolist();
cum_number_of_segments.insert(0,0);

phone_score = [];
x = f.readlines()
lookup_tab = [tmp.rstrip().split(' ') for tmp in x]

# Code for Proposed GoP formulation :
series = pd.Series(number_of_segments)
cum_number_of_segments_tmp = series.cumsum()
cum_number_of_segments=cum_number_of_segments_tmp.tolist()
cum_number_of_segments.insert(0,0)
print(cum_number_of_segments)
phone_score = []
phone_post=[]#<---
# Code for Proposed GoP formulation :
for x in range(len(number_of_segments)):

req_t_id=transition_id[cum_number_of_segments[x]:(cum_number_of_segments[x+1])];
req_t_id.sort();
score=0.0;

req_t_id.sort()
#print("Segment num",x,req_t_id)
score=0.0
tmp_phone_post=[] # <--
for y in range(len(req_t_id)-1):

tmp_prob = float(lookup_tab[req_t_id[y]-1][2]);
tmp_pdf = int(lookup_tab[req_t_id[y]-1][1]);
tmp_post = float(posterior[cum_number_of_segments[x]+y][tmp_pdf]);
score = score + math.log(tmp_prob) + math.log(tmp_post);

tmp_pdf = int(lookup_tab[req_t_id[-1]-1][1]);
tmp_post = float(posterior[cum_number_of_segments[x+1]-1][tmp_pdf]);
score = (score + math.log(tmp_post) + float(number_of_segments[x]-1)*math.log(num_of_senones)) / float(number_of_segments[x]);
phone_score.append(score);

tmp_prob = float(lookup_tab[req_t_id[y]-1][2])
tmp_pdf = int(lookup_tab[req_t_id[y]-1][1])
tmp_post = float(posterior[cum_number_of_segments[x]+y][tmp_pdf])
#print("Senone_ID/Phone no.",x,"Transitions",cum_number_of_segments[x]+y,"PDF",tmp_pdf,tmp_post)
tmp_phone_post.append(tmp_post)# <-----
score = score + math.log(tmp_prob) + math.log(tmp_post)
tmp_pdf = int(lookup_tab[req_t_id[-1]-1][1])
tmp_post = float(posterior[cum_number_of_segments[x+1]-1][tmp_pdf])
tmp_phone_post.append(tmp_post) #<----------
#print("Senone_ID/Phone no.",x,"Transitions",cum_number_of_segments[x+1]-1,"PDF",tmp_pdf,tmp_post,end="\n")
score = (score + math.log(tmp_post) + float(number_of_segments[x]-1)*math.log(num_of_senones)) / float(number_of_segments[x])
phone_post.append(tmp_phone_post)
phone_score.append(score)

# Displaying the scores :
print('Forced aligned phonemes : ');
print(aligned_phones);
print('GOP formulated score of each phoneme : ');
print(phone_score);

# The phoneme_list.txt file contains phoneme's in the 1st column and GoP formulated scores in the 2nd column
print('Forced aligned phonemes : ')
print(aligned_phones)
print('GOP formulated score of each phoneme : ')
print(phone_score)
#print('Posterior probability of each phoneme')
#print(phone_post)

# The phoneme_list.txt file contains phoneme's in the 1st column and GoP formulated scores in the 2nd column
f = open(sys.argv[3], 'w')
for i in range(len(phone_score)):
f.write("%s %f\n" % (aligned_phones[i], phone_score[i]))
f.close()
f=open(sys.argv[3].strip('.txt')+'_phone_posteriors.txt','w+')
for i in range(len(phone_score)):
f.write("%s %s\n" % (aligned_phones[i],str(phone_post[i]).rstrip(']').lstrip('[')))
f.close()