Unity中使用贝叶斯拼写纠错器(基于C#)

10 篇文章 0 订阅
3 篇文章 0 订阅

目录

一、贝叶斯定理(基础)

二、拼写检测器原理

三、编辑距离

四、先验概率P(c)

五、Unity中实现 

六、结果


 

一、贝叶斯定理(基础)

贝叶斯定理:在已知P(A|B)的情况下如何求得P(B|A)。其基本求解公式为:

条件概率:P(A|B)表示事件B已经发生的前提下,事件A发生的概率,叫做事件B发生下事件A的条件概率。

二、拼写检测器原理

给定一个单词 w ,我们的任务是从单词库中选择和它最相似的拼写正确的单词 c 

对应的贝叶斯问题就是,给定一个词 w, 在所有正确的拼写词中, 我们想要找一个单词 c, 使得对于 w 的正确条件概率最大, 也就是说:

其中argmaxc表示,用来枚举所有可能的 c ,并且选取最大的概率。

结合贝叶斯理论,上式即为:

 因为用户可以输错任何词, 因此对于任何 c 来讲, 出现 w 的概率 P(w) 都是一样的, 从而我们在上式中忽略它, 写成:

argmaxc P(c|w) 正比于 argmaxc P(w|c) P(c)

 因此argmaxc P(w|c) P(c)就是编辑距离与P(c)的的乘积

三、编辑距离

编辑距离:两个词之间的编辑距离定义为使用了几次【插入】(在词中插入一个单字母), 【删除】(删除一个单字母), 【交换】(交换相邻两个字母), 【替换】(把一个字母换成另一个)的操作从一个词变到另一个词。

一般情况下,编辑距离为2时已经可以覆盖大部分情况

四、先验概率P(c)

正确单词 c 出现的概率

五、Unity中实现 

1、将词典文件和训练文件放在StreamingAssets文件夹下,便于读取

2、 定义按钮操作和文件位置

    public Button ID_DicTrain_Button;
    public Button ID_Bayes_Button;
    public InputField ID_InputField;

    static Dictionary<string, int> Dic;//词典
     

    static string trainingFile = "training.txt";//训练文件
    static string dicFile = "dic.txt";//根据训练文件生成词典里的单词词频数文件(每个单词出现的次数)
    static string dicOrigin = "Dictionary1.dic";//原词典
    public string IDData_Path = Application.streamingAssetsPath + "/" + trainingFile;//ID数据文件地址
    public string dicData_Path = Application.streamingAssetsPath + "/" + dicFile;//生成词典位置
    public string dicOrigin_Path = Application.streamingAssetsPath + "/" + dicOrigin;//原词典位置

 3、Start函数执行训练词典操作(已存在的话直接加载)

        if (File.Exists(dicData_Path))//文件已存在
        {
            Debug.Log("加载词典中...");
            LoadDic();//读取已经训练完成的词典
            Debug.Log("加载词典完成");
        }
        else
        {
            //训练词典
            Debug.Log("训练词典中...");//词频数文件不存在,根据dic词典训练
            Dic = LoadUSDic();//训练词典
            TrainDic(IDData_Path, Dic);//加载训练文件
            StringBuilder dicBuilder = new StringBuilder();
            foreach (var item in Dic)
            {
                dicBuilder.AppendLine(item.Key + "\t" + item.Value);//单词和其个数
            }
            File.WriteAllText(dicData_Path, dicBuilder.ToString());//写入词频数文件
            var wordCount = Dic.Count;
            Debug.Log("训练完成..." + wordCount);
        }

4、贝叶斯主函数执行

        //输入单词
        var inputWord = ID_InputField.text;//输入的字符
        if (!string.IsNullOrEmpty(inputWord))
        {
            if (Dic.Keys.Contains(inputWord))//如果单词在词典中已经存在,正确
            {
                Debug.Log("你输入的字符 【" + inputWord + "】 是正确的!");
            }
            else//如果不正确,获取建议单词
            {
                var suggestWords = GetSuggestWords(inputWord);
                Debug.Log("候选字符: ");
                foreach (var word in suggestWords)
                {
                    Debug.Log("\t\t\t " + word);
                }
            }
        }
        else
        {
            Debug.Log("没有获取到识别的字符");
        }

5、其它子函数

    /// <summary>
    /// 加载已经训练完成的词典
    /// </summary>
    public void LoadDic()
    {
        Dic = new Dictionary<string, int>();

        var lines = File.ReadAllLines(dicData_Path);//读取已经训练完成的词典

        foreach (var line in lines)
        {
            if (line != "")
            {
                var dicItem = line.Split('\t');
                if (dicItem.Length == 2)
                    Dic.Add(dicItem[0], int.Parse(dicItem[1]));
            }
        }
    }

 

    /// <summary>
    /// 训练词典
    /// </summary>
    /// <param name="IDData_Path"></param>
    /// <param name="ht"></param>
    public void TrainDic(string IDData_Path, Dictionary<string, int> ht)
    {
        StreamReader reader = new StreamReader(IDData_Path);
        string sLine = "";//存放每一个句子

        string pattern = @"[a-zA-Z0-9]+";//正则表达式匹配单词,[a-z] 表示所有小写字母,[A-Z] 表示所有大写字母,[0-9] 表示所有数字

        Regex regex = new Regex(pattern);
        int count = 0;//计算单词的个数

        while (sLine != null)
        {
            sLine = reader.ReadLine();//正则表达式单行匹配模式
            if (sLine != null)
            {
                //sLine = sLine.ToLower().Replace("'", " ");
                var matchWords = regex.Matches(sLine);
                foreach (Match match in matchWords)
                {
                    var word = match.Value;//还是读取的每一行值
                    if (!ht.ContainsKey(word))
                    {
                        count++;
                        ht.Add(word, 1);
                    }
                    else
                    {
                        ht[word]++;
                    }
                }
            }
        }
        reader.Close();
    }

 

    /// <summary>
    /// 从en-US读取词语【词语开始[Words]】
    /// </summary>
    /// <returns></returns>
    public Dictionary<string, int> LoadUSDic()
    {
        var dic = new Dictionary<string, int>();
        string currentSection = "";
        FileStream fs = new FileStream(IDData_Path, FileMode.Open, FileAccess.Read, FileShare.Read);
        StreamReader sr = new StreamReader(fs, Encoding.UTF8);
        Debug.Log("fuck");
        while (sr.Peek() >= 0)//判断读取的文件是否结束,如果结束了会返回int型 -1
        {
            string tempLine = sr.ReadLine().Trim();//读取每一行字符串,Trim()删除字符串头部及尾部出现的空格
            if (tempLine.Length > 0)//当前行的字符长度
            {
                switch (tempLine)//switch表达式
                {
                    case "[Words]"://switch表达式的值和case的值匹配上了,需要执行的代码;
                        currentSection = tempLine;
                        break;
                    default://switch表达式的值和case的值都匹配不上,需要执行的代码;
                        switch (currentSection)
                        {
                            case "[Words]": // dictionary word list
                                            // splits word into its parts
                                string[] parts = tempLine.Split('/');//字符分割成一块块并储存在数组中
                                dic.Add(parts[0], 1);
                                break;
                        } // currentSection swith
                        break;
                } //tempLine switch
            } // if templine
        } // read line
        sr.Close();
        fs.Close();
        return dic;
    }

 

    /// <summary>
    /// 编辑距离为1的词语
    /// </summary>
    /// <param name="word"></param>
    /// <returns></returns>
    public List<string> GetEdits1(string word)
    {
        var n = word.Length;
        var tempWord = "";
        var editsWords = new List<string>();
        for (int i = 0; i < n; i++)//delete一个字母的情况
        {
            tempWord = word.Substring(0, i) + word.Substring(i + 1);
            if (!editsWords.Contains(tempWord))
                editsWords.Add(tempWord);
        }

        for (int i = 0; i < n - 1; i++)//调换transposition一个字母的情况
        {
            tempWord = word.Substring(0, i) + word.Substring(i + 1, 1) + word.Substring(i, 1) + word.Substring(i + 2);
            if (!editsWords.Contains(tempWord))
                tempWord = tempWord.ToUpper();//只要大写字母字符

            editsWords.Add(tempWord);
        }

        for (int i = 0; i < n; i++)//替换replace一个字母的情况
        {
            string t = word.Substring(i, 1);
            for (int ch = 'a'; ch <= 'z'; ch++)
            {
                if (ch != Convert.ToChar(t))
                {
                    tempWord = word.Substring(0, i) + Convert.ToChar(ch) + word.Substring(i + 1);
                    if (!editsWords.Contains(tempWord))
                        tempWord = tempWord.ToUpper();//只要大写字母字符
                    editsWords.Add(tempWord);
                }
            }
        }


        for (int i = 0; i <= n; i++)//insert一个字母的情况
        {
            //string t = word.Substring(i, 1);
            for (int ch = 'a'; ch <= 'z'; ch++)
            {
                tempWord = word.Substring(0, i) + Convert.ToChar(ch) + word.Substring(i);
                if (!editsWords.Contains(tempWord))
                    editsWords.Add(tempWord);
            }
        }

        return editsWords;
    }

 

    /// <summary>
    /// 获取编辑距离为2的单词
    /// </summary>
    /// <param name="word"></param>
    /// <returns></returns>
    public List<string> GetEdits2(string word)
    {
        var words = GetEdits1(word);

        var result = words.AsReadOnly().ToList();

        foreach (var edit in words)
        {
            GetEdits1(edit).ForEach(w =>
            {
                if (Dic.ContainsKey(w))
                {
                    result.Add(w);
                }
            });
        }
        return result;
    }
    /// <summary>
    /// 获取建议词语
    /// </summary>
    /// <param name="word"></param>
    /// <returns></returns>
    public List<string> GetSuggestWords(string word)
    {
        var result = GetEdits1(word).Where(w => Dic.ContainsKey(w)).ToList();//输出编辑距离为1的候选项
                                                                             //result.Add(word);

        if (result.Count == 0)
        {
            result = GetEdits2(word);//输出编辑距离为2的候选项
            if (result.Count == 0)
            {
                result.Add(word);
            }
        }
        // 按先验概率排序
        result = result.OrderByDescending(w => Dic.ContainsKey(w) ? Dic[w] : 1).ToList();
        return result.Take(Math.Min(result.Count, 10)).ToList();//控制输出的个数
    }

 6、挂在unity场景里

六、结果

加载训练词典(已存在,不存在就先训练)

 结果

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值