s3_astar.c

来自「CMU大名鼎鼎的SPHINX-3大词汇量连续语音识别系统」· C语言 代码 · 共 1,267 行 · 第 1/3 页

C
1,267
字号
/* ==================================================================== * Copyright (c) 1995-2004 Carnegie Mellon University.  All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright *    notice, this list of conditions and the following disclaimer.  * * 2. Redistributions in binary form must reproduce the above copyright *    notice, this list of conditions and the following disclaimer in *    the documentation and/or other materials provided with the *    distribution. * * This work was supported in part by funding from the Defense Advanced  * Research Projects Agency and the National Science Foundation of the  * United States of America, and the CMU Sphinx Speech Consortium. * * THIS SOFTWARE IS PROVIDED BY CARNEGIE MELLON UNIVERSITY ``AS IS'' AND  * ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY * NOR ITS EMPLOYEES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ==================================================================== * *//* * astar.c -- A* DAG search to create N-best lists * * ********************************************** * CMU ARPA Speech Project * * Copyright (c) 1996 Carnegie Mellon University. * ALL RIGHTS RESERVED. * ********************************************** *  * HISTORY *  * 27-Feb-1998	M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University * 		Added check in building DAG for avoiding cycles with dagfudge. *  * 08-Sep-97	M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University * 		Added .Z compression option to lattice and nbest files. *  * 22-Nov-96	M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University * 		Added -maxedge argument to control memory usage. * 		Added -maxlmop and -maxlpf options to control execution time. * 		Added -maxppath option to control CPU/memory usage. *  * 18-Nov-96	M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University * 		Added reporting of acoustic and LM scores for each word in each hyp. * 		Changed LM scores in nbest files to be unscaled (i.e., without any * 		language weight or insertion penalty. *  * 09-Nov-96	M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University * 		Added code in dag_link_bypass to update an existing link, if any, * 		instead of adding several bypass links between the same two nodes. * 		This reduces CPU and memory requirements considerably. *  * 05-Nov-96	M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University * 		Added -dagfudge and -min_endfr parameter handling. *  * 28-Oct-96	M K Ravishankar (rkm@cs.cmu.edu) at Carnegie Mellon University * 		Started, copying from nbest.c. *//** \file s3_astar.c * \brief engine for s3.0 astar */#include <stdio.h>#include <stdlib.h>#include <string.h>#include <assert.h>#include <s3types.h>#include "s3_dag.h"#include <mdef.h>#include <tmat.h>#include <dict.h>#include <lm.h>#include <fillpen.h>#include <search.h>#include <logs3.h>static dag_t dag;static s3wid_t startwid;	/* Begin silence */static s3wid_t finishwid;	/* End silence */static int32 beam;/**************************************** * Globals!   This is a hack!		* ****************************************/dict_t *dict;		/* The dictionary */lm_t *lm;		/* The Language model */fillpen_t *fpen;	/* The filler penalty structure. */s3lmwid_t *dict2lmwid;	/* Mapping from decoding dictionary wid's to lm ones.  They may not be the same! */static int32 maxlmop;		/* Max LM ops allowed before utterance aborted */static int32 lmop;		/* #LM ops actually made */static int32 maxedge;		/* Max #edges in DAG allowed before utterance aborted */static int32 filler_word (s3wid_t w){    if ((w == startwid) || (w == finishwid))	return 0;    if ((w >= dict->filler_start) && (w <= dict->filler_end))	return 1;    return 0;}/** * Link two DAG nodes with the given arguments * Return value: 0 if successful, -1 if maxedge limit exceeded. */static int32 dag_link (dagnode_t *pd, dagnode_t *d, int32 ascr){    daglink_t *l;        /* Link d into successor list for pd */    l = (daglink_t *) listelem_alloc (sizeof(daglink_t));    l->node = d;    l->ascr = ascr;    l->is_filler_bypass = 0;    l->next = pd->succlist;    pd->succlist = l;    l = (daglink_t *) listelem_alloc (sizeof(daglink_t));    l->node = pd;    l->ascr = ascr;    l->is_filler_bypass = 0;    l->next = d->predlist;    d->predlist = l;    dag.nlink++;    return (dag.nlink > maxedge) ? -1 : 0;}/** * Add a filler-bypass link between the two nodes or update an existing bypass link if * the new score is better. * Return value: 0 if successful, -1 if maxedge limit exceeded. */static int32 dag_link_bypass (dagnode_t *pd, dagnode_t *d, int32 ascr){    daglink_t *l;        /* Find the existing bypass link, if any, between pd and d */    for (l = pd->succlist; l && ((! l->is_filler_bypass) || (l->node != d)); l = l->next);    if (! l) {	/* No existing bypass link; create one from pd to d */	l = (daglink_t *) listelem_alloc (sizeof(daglink_t));	l->node = d;	l->ascr = ascr;	l->is_filler_bypass = 1;	l->next = pd->succlist;	pd->succlist = l;		dag.nlink++;	dag.nbypass++;    } else if (l->ascr < ascr) {	/* Link pd -> d exists; update link score for it */	l->ascr = ascr;    }    return (dag.nlink > maxedge) ? -1 : 0;}static int32 dag_param_read (FILE *fp, char *param, int32 *lineno){    char line[1024], wd[1024];    int32 n;        while (fgets (line, 1024, fp) != NULL) {	(*lineno)++;	if (line[0] == '#')	    continue;	if ((sscanf (line, "%s %d", wd, &n) == 2) && (strcmp (wd, param) == 0))	    return n;    }    return -1;}#if 0static void daglinks_dump (dagnode_t *d){    daglink_t *l;        for (l = d->succlist; l; l = l->next)	printf ("%6d %5d %12d %s\n", l->node->seqid, l->node->sf, l->ascr,		dict_wordstr (dict, l->node->wid));}#endif/** * Mark every node that has a path to the argument dagnode as "reachable". */static void dag_mark_reachable (dagnode_t *d){    daglink_t *l;        d->reachable = 1;    for (l = d->predlist; l; l = l->next)	if (! l->node->reachable)	    dag_mark_reachable (l->node);}static void dag_remove_unreachable ( void ){    dagnode_t *d;    daglink_t *l, *pl, *nl;        for (d = dag.list; d; d = d->alloc_next) {	if (! d->reachable) {	    /* Remove successor node links */	    for (l = d->succlist; l; l = nl) {		nl = l->next;		listelem_free ((char *) l, sizeof(daglink_t));	    }	    d->succlist = NULL;	    /* Remove predecessor links */	    for (l = d->predlist; l; l = nl) {		nl = l->next;		listelem_free ((char *) l, sizeof(daglink_t));	    }	    d->predlist = NULL;	} else {	    /* Remove successor links to unreachable nodes; predecessors are reachable */	    pl = NULL;	    for (l = d->succlist; l; l = nl) {		nl = l->next;		if (! l->node->reachable) {		    if (! pl)			d->succlist = nl;		    else			pl->next = nl;		    listelem_free ((char *) l, sizeof(daglink_t));		} else		    pl = l;	    }	}    }}/** * Add auxiliary links bypassing filler nodes in DAG.  In principle, a new such * auxiliary link can end up at ANOTHER filler node, and the process must be repeated * for complete transitive closure.  But removing fillers in the order in which they * appear in dag.list ensures that succeeding fillers have already been bypassed. * (See comment before dag_load.) * Return value: 0 if successful; -1 if DAG maxedge limit exceeded. */static int32 dag_bypass_filler_nodes ( void ){    dagnode_t *d, *pnode, *snode;    daglink_t *plink, *slink;    int32 scr;        /* Create additional links in DAG bypassing filler nodes */    for (d = dag.list; d; d = d->alloc_next) {	if (! filler_word (d->wid))	/* No need to bypass this node */	    continue;		/* For each link TO d add a link to d's successors */	for (plink = d->predlist; plink; plink = plink->next) {	    pnode = plink->node;	    scr = plink->ascr + fillpen(fpen, dict_basewid (dict, d->wid));	    	    /* Link this predecessor of d to successors of d */	    for (slink = d->succlist; slink; slink = slink->next) {		snode = slink->node;		/* Link only to non-filler successors; fillers have been bypassed */		if (! filler_word (snode->wid))		    if (dag_link_bypass (pnode, snode, scr + slink->ascr) < 0)			return -1;	    }	}    }    return 0;}/** * For each link compute the heuristic score (hscr) from the END of the link to the * end of the utterance; i.e. the best score from the end of the link to the dag * exit node. */static void dag_compute_hscr ( void ){    dagnode_t *d, *d1, *d2;    daglink_t *l1, *l2;    s3wid_t bw0, bw1, bw2;    int32 hscr, best_hscr;        for (d = dag.list; d; d = d->alloc_next) {	bw0 = filler_word (d->wid) ? BAD_S3WID : dict_basewid (dict, d->wid);	/* For each link from d, compute heuristic score */	for (l1 = d->succlist; l1; l1 = l1->next) {	    assert (l1->node->reachable);	    d1 = l1->node;	    if (d1 == dag.exit.node)		l1->hscr = 0;	    else {		bw1 = filler_word (d1->wid) ? BAD_S3WID : dict_basewid (dict, d1->wid);		if (NOT_S3WID(bw1)) {		    bw1 = bw0;		    bw0 = BAD_S3WID;		}				best_hscr = (int32)0x80000000;		for (l2 = d1->succlist; l2; l2 = l2->next) {		    d2 = l2->node;		    if (filler_word (d2->wid))			continue;		    		    bw2 = dict_basewid (dict, d2->wid);		    hscr = l2->hscr + l2->ascr + lm_tg_score (lm, dict2lmwid[bw0], dict2lmwid[bw1], dict2lmwid[bw2], bw2);		    		    if (hscr > best_hscr)			best_hscr = hscr;		}		l1->hscr = best_hscr;	    }	}    }}static void dag_remove_bypass_links ( void ){    dagnode_t *d;    daglink_t *l, *pl, *nl;        for (d = dag.list; d; d = d->alloc_next) {	pl = NULL;	for (l = d->succlist; l; l = nl) {	    nl = l->next;	    if (l->is_filler_bypass) {		if (! pl)		    d->succlist = nl;		else		    pl->next = nl;		listelem_free ((char *) l, sizeof(daglink_t));	    } else		pl = l;	}    }}/** * Load a DAG from a file: each unique <word-id,start-frame> is a node, i.e. with * a single start time but it can represent several end times.  Links are created * whenever nodes are adjacent in time. * dagnodes_list = linear list of DAG nodes allocated, ordered such that nodes earlier * in the list can follow nodes later in the list, but not vice versa:  Let two DAG * nodes d1 and d2 have start times sf1 and sf2, and end time ranges [fef1..lef1] and * [fef2..lef2] respectively.  If d1 appears later than d2 in dag.list, then * fef2 >= fef1, because d2 showed up later in the word lattice.  If there is a DAG * edge from d1 to d2, then sf1 > fef2.  But fef2 >= fef1, so sf1 > fef1.  Reductio ad * absurdum. * Return value: 0 if successful, -1 otherwise. */int32 s3astar_dag_load (char *file){    FILE *fp;    char line[16384], wd[1024];    int32 nfrm, nnode, sf, fef, lef, ef, lineno;    int32 i, j, k, l, final, seqid, from, to, ascr;    int32 fudge, min_ef_range;    dagnode_t *d, *pd, *tail, **darray;    s3wid_t w;    struct lat_s {	dagnode_t *node;	int32 ef;	int32 ascr;    } *lat;		/* Lattice (bptable) entries in each frame */    int32 *frm2lat;	/* frm2lat[f] = first lattice entry for frame f */    float32 lb, f32arg;    int32 ispipe;        /* Set limit on max DAG edges allowed after which utterance is aborted */    maxedge = *((int32 *) cmd_ln_access ("-maxedge"));        dag.list = NULL;    dag.nlink = 0;    dag.nbypass = 0;        tail = NULL;    darray = NULL;    lat = NULL;    frm2lat = NULL;    lineno = 0;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?