From 4814eaf938f912ad08debc55db649fb2f671e645 Mon Sep 17 00:00:00 2001 From: Todd Bilsborrow <todd@synapse-ai.com> Date: Wed, 30 Mar 2016 11:16:05 -0700 Subject: [PATCH] Estimate confidence numbers for hypotheses, based on Kaldi's likelihood numbers. Confidence algorithm borrowed from the sample_full_post_processor commits 2d1be9d and ea87b6a, and implemented in the main master/worker code. --- kaldigstserver/master_server.py | 14 +++++++++----- kaldigstserver/worker.py | 18 +++++++++++++++++- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/kaldigstserver/master_server.py b/kaldigstserver/master_server.py index d7883c9..b634703 100644 --- a/kaldigstserver/master_server.py +++ b/kaldigstserver/master_server.py @@ -114,7 +114,7 @@ class HttpChunkedRecognizeHandler(tornado.web.RequestHandler): def prepare(self): self.id = str(uuid.uuid4()) - self.final_hyp = "" + self.final_hyp = {"transcript":"", "num_segments":0, "total_confidence":0.0} self.final_result_queue = Queue() self.user_id = self.request.headers.get("device-id", "none") self.content_id = self.request.headers.get("content-id", "none") @@ -165,7 +165,9 @@ def end_request(self, *args, **kwargs): hyp = yield tornado.gen.Task(self.get_final_hyp) if self.error_status == 0: logging.info("%s: Final hyp: %s" % (self.id, hyp)) - response = {"status" : 0, "id": self.id, "hypotheses": [{"utterance" : hyp}]} + # overall confidence is the average of all segments' confidences + hyp_confidence = 0 if hyp["num_segments"] == 0 else hyp["total_confidence"] / hyp["num_segments"] + response = {"status" : 0, "id": self.id, "hypotheses": [{"utterance" : hyp["transcript"], ":confidence": hyp_confidence}]} self.write(response) else: logging.info("%s: Error (status=%d) processing HTTP request: %s" % (self.id, self.error_status, self.error_message)) @@ -186,9 +188,11 @@ def send_event(self, event): if event["status"] == 0 and ("result" in event): try: if len(event["result"]["hypotheses"]) > 0 and event["result"]["final"]: - if len(self.final_hyp) > 0: - self.final_hyp += " " - self.final_hyp += event["result"]["hypotheses"][0]["transcript"] + if len(self.final_hyp["transcript"]) != 0: + self.final_hyp["transcript"] += " " + self.final_hyp["transcript"] += event["result"]["hypotheses"][0]["transcript"] + self.final_hyp["num_segments"] += 1 + self.final_hyp["total_confidence"] += event["result"]["hypotheses"][0]["confidence"] except: e = sys.exc_info()[0] logging.warn("Failed to extract hypothesis from recognition result:" + e) diff --git a/kaldigstserver/worker.py b/kaldigstserver/worker.py index 6dc79a5..c9a4005 100644 --- a/kaldigstserver/worker.py +++ b/kaldigstserver/worker.py @@ -15,6 +15,7 @@ import zlib import base64 import time +from math import exp from ws4py.client.threadedclient import WebSocketClient @@ -189,13 +190,28 @@ def _on_full_result(self, full_result_json): full_result = json.loads(full_result_json) full_result['segment'] = self.num_segments if full_result.get("status", -1) == common.STATUS_SUCCESS: + + # confidence estimation (requires hypotheses in descending likelihood order) + hypotheses = full_result["result"]["hypotheses"] + last_confidence = 1 + for h in [hypotheses[i:i+2] for i in xrange(len(hypotheses))]: + # iterate over sliding window of size 2, including a 1-element slice at the end + if len(h) == 1: + # this is the last hypothesis (also could be the only one) + h[0]["confidence"] = last_confidence + else: + # otherwise confidence is based on how far away the following likelihood is + # but no larger than the previous confidence + curr_hyp, next_hyp = h[0], h[1] + last_confidence = min(last_confidence, 1 - exp(next_hyp["likelihood"] - curr_hyp["likelihood"])) + curr_hyp["confidence"] = last_confidence + #logger.info("%s: Postprocessing (final=%s) result.." % (self.request_id, final)) logger.debug("%s: Before postprocessing: %s" % (self.request_id, full_result)) full_result = self.post_process_full(full_result) logger.info("%s: Postprocessing done." % self.request_id) logger.debug("%s: After postprocessing: %s" % (self.request_id, full_result)) - try: self.send(json.dumps(full_result)) except: