Files

204 lines
5.6 KiB
Python

#!/usr/bin/env python
import sys
import numpy
import struct
from cmusphinx import s3mixw
def perplexity(dist):
return numpy.exp(-(dist * numpy.log(dist)).sum())
def prune_mixw_entropy(mixw, avgn):
# Calculate average entropy
avgp = 0
count = 0
for m in mixw:
for f in m:
pplx = perplexity(f)
avgp += pplx
count += 1
avgp /= count
scale = float(avgn) / avgp
avgtop = 0
mintop = 999
maxtop = 0
histo = numpy.zeros(len(mixw[0, 0]), 'i')
for m in mixw:
for f in m:
pplx = perplexity(f)
top = round(pplx * scale)
if top < len(f):
histo[top] += 1
avgtop += top
if top < mintop:
mintop = top
if top > maxtop:
maxtop = top
f.put(f.argsort()[:-top], 0)
print("Average #mixw: %.2f" % (float(avgtop) / count))
print("Min #mixw: %d Max #mixw: %d" % (mintop, maxtop))
return histo
def prune_mixw_entropy_min(mixw, avgn, minn):
# Calculate average entropy
avgp = 0
count = 0
for m in mixw:
for f in m:
pplx = perplexity(f)
avgp += pplx
count += 1
avgp /= count
scale = float(avgn) / avgp
avgtop = 0
mintop = 999
maxtop = 0
histo = numpy.zeros(len(mixw[0, 0]), 'i')
for m in mixw:
for f in m:
pplx = perplexity(f)
top = round(pplx * scale)
if top < minn:
top = minn
elif top >= len(f):
top = len(f) - 1
else:
histo[top] += 1
avgtop += top
if top < mintop:
mintop = top
if top > maxtop:
maxtop = top
f.put(f.argsort()[:-top], 0)
print("Average #mixw: %.2f" % (float(avgtop) / count))
print("Min #mixw: %d Max #mixw: %d" % (mintop, maxtop))
return histo
def prune_mixw_pplx_hist(mixw):
# Calculate perplexity histogram
histo = numpy.zeros(len(mixw[0, 0]), 'i')
for m in mixw:
for f in m:
pplx = perplexity(f)
histo[round(pplx)] += 1
# Floor number of mixture weights at the mode of perplexity
minn = histo.argmax()
avgtop = 0
mintop = 999
maxtop = 0
for m in mixw:
for f in m:
top = round(perplexity(f))
avgtop += top
if top < minn:
top = minn
if top < mintop:
mintop = top
if top > maxtop:
maxtop = top
f.put(f.argsort()[:-top], 0)
count = mixw.shape[0] * mixw.shape[1]
print("Average #mixw: %.2f" % (float(avgtop) / count))
print("Min #mixw: %d Max #mixw: %d" % (mintop, maxtop))
return histo
def prune_mixw_topn(mixw, n):
for m in mixw:
for f in m:
f.put(f.argsort()[:-n], 0)
def prune_mixw_thresh(mixw, thresh):
avgtop = 0
mintop = 999
maxtop = 0
histo = numpy.zeros(len(mixw[0, 0]), 'i')
for m in mixw:
for f in m:
toprune = numpy.less(f, thresh).nonzero()[0]
top = len(f) - len(toprune)
histo[top] += 1
avgtop += top
if top < mintop:
mintop = top
if top > maxtop:
maxtop = top
f.put(toprune, 0)
count = mixw.shape[0] * mixw.shape[1]
print("Average #mixw: %.2f" % (float(avgtop) / count))
print("Min #mixw: %d Max #mixw: %d" % (mintop, maxtop))
return histo
def norm_floor_mixw(mixw, floor=1e-7):
return (mixw.T / mixw.T.sum(0)).T.clip(floor, 1.0)
fmtdesc = \
"""BEGIN FILE FORMAT DESCRIPTION
(int32) <length(string)> (including trailing 0)
<string> (including trailing 0)
... preceding 2 items repeated any number of times
(int32) 0 (length(string)=0 terminates the header)
(int32) <#components>
(int32) <#gmms>
#gmms (unsigned char) quantized mixture weights for feature-0 gmm-0
preceding 2 items repeated feature_count times.
preceding 4 items repeated codebook_count times.
END FILE FORMAT DESCRIPTION
cluster_count 0
logbase 1.0001
codebook_count 1
feature_count %d"""
def write_sendump(mixw, outfile, floor=1e-7):
n_sen, n_feat, n_gau = mixw.shape
fh = open(outfile, "wb")
# Write the header
fmtdesc0 = fmtdesc % (n_feat)
for line in fmtdesc0.split('\n'):
fh.write(struct.pack('>I', len(line) + 1))
fh.write(line)
fh.write('\0')
# Align to 4 bytes
k = fh.tell() & 3
if k > 0:
k = 4 - k
fh.write(struct.pack('>I', k))
fh.write('!' * k)
fh.write(struct.pack('>I', 0))
# Align number of senones to 4 bytes
aligned_n_sen = (n_sen + 3) & ~3
fh.write(struct.pack('>I', n_gau))
fh.write(struct.pack('>I', aligned_n_sen))
# Write them out transposed and quantized (could be much faster)
if floor == 0.0:
# Assume they are already normalized and floored
qmixw = (-numpy.log(mixw) / numpy.log(1.0001)).astype('i') >> 10
else:
qmixw = (-numpy.log(norm_floor_mixw(mixw, floor)) /
numpy.log(1.0001)).astype('i') >> 10
qmixw = qmixw.clip(0, 159).astype('uint8')
for f in range(0, n_feat):
for d in range(0, n_gau):
qmixw[:, f, d].tofile(fh)
# Align it to 4 byte boundary (why?)
if aligned_n_sen > n_sen:
fh.write('\0' * (aligned_n_sen - n_sen))
fh.close()
if __name__ == '__main__':
ifn, ofn, tmw, mmw = sys.argv[1:]
tmw = int(tmw)
mmw = int(mmw)
mixw = norm_floor_mixw(s3mixw.open(ifn).getall())
prune_mixw_entropy_min(mixw, tmw, mmw)
write_sendump(mixw, ofn)