[Unity语音识别项目续]字符串匹配算法

      上次写了个语音识别的博客,能够识别录制的音频文件并转成一个字符串。效果很好,而且还是识别的中文。不过很快又意识到几个问题:

       1.你不能指望每次翻译的都那么准确,或者说,一字不差。

       2.口语化的表达使得往往一大堆的语句其实是一个意思。

      所以我们不难发现一个事实,如果语音识别要拿来用的话,这种对应最好不要是一一映射的,就比如,如果我只识别到了玩家说了“攻击”,才让我们的游戏物体做出相应的操作的话,那么问题来了,万一玩家实际说的是“进攻”,抑或是更口语化的表达,又或者说是说了攻击但被识别成了“公鸡”,那怎么办呢?难道就不执行这种操作了吗?这显然是不行的。

     于是便有了两个想法:建立一张等价表,这是一张多对一的映射表,多种输入可以达成同一种输出,其次,对于一个字符串,只需要字符相似度达到一定的时候,就认为是同一个句子。

    思路有了,很快就能撸出一个字符串配对的小系统,以下是代码:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace STRINGTOINSTRUCT
{
	public class stringsystem
	{
		public float EndureWrongRate;
		public int MAX_INSTRUCT_COUNT;
		public int MAX_DEPENCE_COUNT;
		private int Temp_Instruct_Count;
		private int Temp_Depence_Count;
		private int[] InstructTable;
		private string[] Find_Table_String;
		private int[] Find_Table_Instruct;
		public stringsystem()
		{
			EndureWrongRate = 0.5f;
			MAX_INSTRUCT_COUNT = 15;
			MAX_DEPENCE_COUNT = 1000;
			Temp_Instruct_Count = 0;
			Temp_Depence_Count = 0;
			InstructTable = new int[MAX_INSTRUCT_COUNT];
			Find_Table_String=new string[MAX_DEPENCE_COUNT];
			Find_Table_Instruct = new int[MAX_DEPENCE_COUNT];
		}
		public int Find(string s,ref float pairrate)
		{
			float Temp_Max_PairRate=0;
			int index=0;
			for(int i=0;i<Temp_Depence_Count;i++)
			{
				float temp_pair=Pair(Find_Table_String[i],s);
				if(temp_pair>Temp_Max_PairRate)
				{
					Temp_Max_PairRate = temp_pair;
					index = i;
				}
			}
			if (Temp_Max_PairRate >= EndureWrongRate)
			{
				pairrate = Temp_Max_PairRate;
				return Find_Table_Instruct [index];
			}
			else
			{
				pairrate = Temp_Max_PairRate;
				return -1;	
			}
		}
		public int InsertInstruct()
		{
			int instructcode = Random.Range (0,10000000);
			InstructTable [Temp_Instruct_Count] = instructcode;
			Temp_Instruct_Count++;
			return instructcode;
		}
		public void Insert_Index(int instruct,string word)
		{
			Find_Table_Instruct [Temp_Depence_Count] = instruct;
			Find_Table_String [Temp_Depence_Count] = word;
			Temp_Depence_Count++;
		}
		private float Pair(string str1,string str2)
		{
			int len1 = str1.Length;
			int len2 = str2.Length;
			int[,] dif = new int[len1+1, len2+1];
			for(int i=0;i<len1+1;i++)
			{
				dif [i, 0] = i;
			}
			for(int i=0;i<len2+1;i++)
			{
				dif [0, i] = i;
			}
			int temp;
			for(int i=1;i<len1+1;i++)
			{
				for(int j=1;j<len2+1;j++)
				{
					if (str1 [i-1] == str2 [j-1]) 
					{
						temp = 0;
					}
					else
					{
						temp = 1;
					}
					dif [i, j] = min (dif[i-1,j-1]+temp,dif[i,j-1]+1,dif[i-1,j]+1);
				}
			}
			float similarity = 1 - (float)dif [len1, len2]/Mathf.Max(str1.Length,str2.Length);
			return similarity;
		}
		private static int min(params int [] arr)
		{
			int min = int.MaxValue;
			foreach(int ar in arr)
			{
				if (ar < min) 
				{
					min = ar;
				}
			}
			return min;
		}
	} 	
};

主要说说其中的pair方法:

其本质上是通过计算有多少不同的字符和总字符的比值算出的相似性。

最开始维护一个二维数组,这个二维数组是有意义的索引为[i][j]的数据存的是字符串1前i个和字符串2的前j个中字符的差异个数。

所以在初始化的时候,我们能确定的是d[0][i]=i;d[i][0]=i;

接着一个双层循环就是一个很简单的动态规划了,状态转移方程是

dif [i, j] = min (dif[i-1,j-1]+temp,dif[i,j-1]+1,dif[i-1,j]+1);

很容易理解,dif[i][j]的值有且只有三种来源,dif[i-1][j-1]加上第i是不是等于第j,或者[i-1][j]+1(因为多出的那一个必然是不一样的,没字符和他配对啊),或者[i][j-1]+1(同理了)

最后就是输出一下dif[len1][len2]即可了。

结合上一篇所讲的从excel中读数据,我们就可以先在excel中写好我们要建立的等价表,再读到程序中,然后把每次语音识别的字符串在这里面进行查询,如果匹配成功,就让游戏物体执行对应的操作,这样,我们的语音识别系统就基本完成了。

下面是一些测试字符串配对系统的结果:


没截图完,反正大概就是输出了一下配对相似性的结果。


猜你喜欢

转载自blog.csdn.net/qq_33999892/article/details/71897594