2018-11-22 17:29:31 m0_38055352 阅读数 1332
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    31035 人正在学习 去看看 张中强

一、概述

      对于语音识别来说,大体上就分为三个方面,一个是声学模型(acoustical model)的训练,一个是语言模型(language model)的训练,最后就是对给定一段语音的解码了,当然,咱们今天讨论的是第二部分,其他的就先丢到一边吧!(在这给大家打一打气,其实语言模型是这三个方面里最复杂的部分了,这部分搞懂之后,其他的也就so easy啦)
      现如今,无论是从流行程度还是经典程度来说,n-gram模型都可以说排在前面,而且思想也比较好理解,比较适合入门,这篇博文和之后的代码实现也都是基于此。


二、n-gram语言模型

      话不多说,先来简要介绍一下n-gram语言模型吧。
n-gram的作用
      先说一说它的作用,语音识别,就是要把一串连续的语音转变为一个合理的句子,那么很容易想到的就可以分为两个步骤,先根据语音中的帧识别出音素,如果对于中文的话就是拼音,然后再根据拼音转化为语义正确的汉字,那么前一部分比较好理解,就找到大量的语音,然后标注好每个语音中所具有的拼音,常规的机器学习的思想,当然这也是有特定的模型的,最经典的就是hmm-gmm模型,当然还有各种各样的神经网络模型,感兴趣的同学可以去查看,但关键是,就算你成功识别出了拼音,但也没有音调,有的就是类似wo ni ta这样形式的拼音,如何把它组合成我们日常生活中的句子呢?
n-gram的原理
      这就要用到我们的语言模型了,假定S表示一个有意义的句子,它由一串特定顺序排列的词(w1,w2,…,wm)组成,m表示句子的长度,即单词个数。计算S在整个语料库中出现的可能性P(s),或表示成P(w1,w2,…,wm),可根据链式法则分解为:
n-gram
其中P(w2|w1)就表示当w1出现了,w2再出现的概率,P(w3|w1w2)就表示当w1w2同时出现了,w3再在他们之后出现的概率,之后的以此类推,这就是最原始的n-gram模型,但这个概率是不好算的,你要一直统计前m-1个字出现了,Wm出现的概率。
      由此就出现了诸如1-gram、2-gram模型,聪明的你应该可以想到了,对于1-gram模型,其计算公式就是:
1-gram
2-gram模型计算公式就是:
2-gram
      这些说起来是个模型,但其实就是一个很简单的统计概率问题,最重要的部分还是对数据的爬取以及对其进行解码的优化。虽然解码思路也很简单,但如果直接暴力解码的话是一个NLP问题,所以要寻找合适的方法解决,关于数据的爬取,如果有同学没有什么思路的话可以关注我之后的博文,会专门讲解一个简单使用的每日百万级数据的Python程序。


三、python代码实现过程详解

      接下来,就开始上干货了,我会大致讲解一下我这一部分的实现思路,然后在最后附上我的python源码,需要的同学可以下载下去再研究。
      首先,咱们大体把你要实现的东西梳理一下。

  • 统计数据库中相邻两个汉字各种情况出现的次数
  • 用上述统计出来的结果生成2-gram模型,也就是将频数转换为频率
  • 如果需要进行平滑处理的话,还需要编写平滑处理的代码
  • 最后,完成2-gram模型统计后,为了验证成果,写一个小的demo,这个demo就类似于一个简单的输入法。

      OK,理清了这四件我们要完成的事情之后,让我们分别来干掉他们。
统计数据库中相邻汉字的频数
      首先,对于第一个任务,要统计数据库中的相邻汉字的频数,这其实还是很好实现的,前提是你选择了合适的数据库。在这次的程序中我选择了MongoDB存储我爬虫爬取到的文本数据,遍历这些数据很容易,就和Python中字典的使用方法差不多,而对于取到文本之后的工作,就不用我说了吧,就是对字符串的操作。
      比如"今天天气真好啊"这句话,你需要做的就是分别统计"今天"、“天天”、"天气"等等,再继续下去,好像有点问题哈,"气真"不是个词的样子,但没关系,这是在分词之前的一个实现,自然就没有考虑词的问题,之后加入了分词之后,这个问题就不在存在了,或者说就减少了。然后将这样统计的结果存入一个字典类似的东西,然后再最后写入磁盘就OK啦
      下面贴出我实现的部分代码:
twoGram_improve/run.py(其中用于统计的部分)

def statistics():
    """
    用于统计数据库中相邻两词出现次数,并存入字典
    :return: 具有所有字词统计次数的字典
    """
    result = {}
    count = 0
    for content in table.find().batch_size(500):
        content = content['content'].strip()
        count += 1
        i = 0
        for i in range(len(content)-2):
            if is_Chinese(content[i]) and is_Chinese(content[i+1]):
                pText = '{0}{1}'.format(content[i], content[i + 1])
                if pText in result:
                    result[pText] += 1
                else:
                    result[pText] = 1
        print('统计完成数据: {0}'.format(count))
        # print('完成统计: {0}.'.format(content))
    return result

twoGram_improve/run.py(其中用于写入磁盘的部分)

def write_to_file(filename, result):
    with open(filename, 'w') as fw:
        for key, value in result.items():
            fw.write('\'{0}\'在数据库中出现的次数为: {1}\n'.format(key, value))
        fw.close()

平滑操作
      关于平滑操作的原理,我在这里就不过多赘述了,但我之前总结过一片博文,大致讲述了一下平滑操作的目的和原理,可以参考【总结】几个简单语言模型平滑方法,这里只给出一个极为简单的Laplace平滑变换(其实就是加1)。
twoGram_improve\utils\Smooth.py(Laplace平滑部分)

def Laplace(filename):
    pattern = re.compile(r'(.*?)\.txt')
    forename = re.search(pattern, filename)
    if forename:
        forename = forename.group(1)
        newfilename = forename + '-Laplace.txt'
        fw = open(newfilename, 'w')
        with open(filename, 'r') as fr:
            pattern = re.compile(': (\d+)')
            patternWord = re.compile('(.*?)\d+')
            for line in fr.readlines():
                perNum = re.search(pattern, line)
                perWord = re.search(patternWord, line)
                if perNum and perWord:
                    perNum = int(perNum.group(1)) + 1
                    perWord = str(perWord.group(1))
                    fw.write(perWord + str(perNum) + '\n')
            fr.close()
            fw.close()

根据统计结果生成2-gram模型
      现在到了重点,终于要生成模型了,既然都已经了解原理了,这部分的工作也就显得没有那么复杂了,其实就是要把频数转换为频率存储起来,那么首先要做的就是计算总数喽!
twoGram_improve\generateTwoGram.py

def get_all():
    with open(r_filename, 'r') as fr:
        pattern = re.compile(r': (\d+)')
        num = 0
        for line in fr.readlines():
            perNum = re.search(pattern, line)
            if perNum:
                perNum = int(perNum.group(1))
                num += perNum
        return num

然后就是生成频率,其实就是把之前统计频数的文件读入,然后再将他们除以总数存入即可。
twoGram_improve\generateTwoGram.py

def get_frequency(all):
    """
    获取2-gram
    :param all: 总字数
    :return: None
    """
    fw = open(w_filename, 'w')
    with open(r_filename, 'r') as fr:
        patternNum = re.compile(r': (\d+)')
        patternWord = re.compile(r'\'([\u4e00-\u9fa5]+)\'')
        for line in fr.readlines():
            perNum = re.search(patternNum, line)
            perWord = re.search(patternWord, line)
            if perNum and perWord:
                perNum = int(perNum.group(1))/all
                perWord = perWord.group(1)
                fw.write('P({0}|{1}) = {2}\n'.format(perWord[1], perWord[0], perNum))
        fr.close()
        fw.close()

验证加demo
      截至到现在,我们的2-gram未分词的语言模型就建立完成了,是不是感觉很简单?但前面我只说那是重点,并没有说是难点,难点其实在解码部分呢!
      我们试想一下,当我给你一句话的拼音的时候,你要怎么根据2-gram模型来生成一句完整的汉字呢?肯定是先匹配第一个拼音,看看其对应的汉字是哪些,然后看第二个拼音,找到第二个拼音对应的所有汉字,对其两两计算概率,然后对之后的每一个拼音也要重复的进行这个过程,并且把其概率乘起来,得到最终的所有可能情况的概率,再比较大小,把概率最大的那一个输出,对不对?用图来描述的话就是下面这个样子。
在这里插入图片描述
      这只是一个示意图,那么假设每一列有20个字的话,如果你输入了10个拼音,那么需要计算的次数就是20的10次方!这是一般的计算机远远承受不了的,其实这就是一个NLP问题,靠穷举是完成不了的。
      在这里我参考了微软输入法的方法,一种类似动态规划的思想,就是每次就计算20*20,也就是当前两个字的概率,对于以上图示过程来说,就是,首先计算"jin"和"tian"的各个汉字组合概率,假设jin对应20个汉字,然后对于"tian"中的每一个汉字,会计算出20个概率,取其中最大的一个,存入一个实现给定的数组中,然后接着计算"tian"和"tian",用同样的方法计算,这样,多利用了一个二维数组,就将一个n×n×n×n…的问题,变成了n×n+n×n+n×n的问题,大大减少了问题的复杂度。
twoGram_improve\demo\run.py

def function_p(dict, result):
    while True:
        pinyin = str(input('请输入拼音: '))
        if pinyin == 'exit':
            break
        li = split_word(pinyin)  # 将各个拼音分开
        M = 0  # 词表的列数     每个拼音可能出现的最多汉字数
        T = len(li)  # 词表的行数     即拼音的个数
        table = []  # 词表
        for item in li:  # 求得词表   以及词表的 M 参数
            pattern = re.compile('\n' + str(item) + '=([\u4e00-\u9fa5]+)')
            characters = re.search(pattern, result)
            if characters:
                characters = characters.group(1)
                if len(characters) > M:
                    M = len(characters)
                table.append(characters)

        prob = [[-1000000 for i in range(M)] for i in range(T)]  # 存储此时对应的最大概率
        ptr = [[0 for i in range(M)] for i in range(T)]  # 存储最大概率的路径

        if not table:  # 如果输入的不是合法的拼音或没输入则返回重新输入
            continue
        for j in range(len(table[0])):  # 将第一个拼音初始化
            ptr[0][j] = j
            prob[0][j] = 0

        for i in range(1, T):  # 对于每一个拼音   也就是词表的每一行
            for j in range(0, len(table[i])):  # 对于当前行的每一个汉字
                maxP = -1000000
                idx = 0
                for k in range(0, len(table[i - 1])):  # 遍历当前拼音的前一个拼音对应的所有汉字
                    if table[i - 1][k] + table[i][j] not in dict:  # 若在统计的词典中不存在 则加入
                        dict[table[i - 1][k] + table[i][j]] = -16.57585272128594
                    thisP = prob[i - 1][k] + dict[table[i - 1][k] + table[i][j]]
                    if thisP > maxP:
                        maxP = thisP
                        idx = k
                prob[i][j] = maxP
                ptr[i][j] = idx

        # 获取最后一行最大值的索引
        maxP = max(prob[T - 1])
        idx = prob[T - 1].index(maxP)

        # 输出结果
        i = T - 1
        string = ''
        while i >= 0:
            string += table[i][idx]
            idx = ptr[i][idx]
            i -= 1

        # 字符串逆序输出
        print(string[::-1])

      到这里,全部的功能我们就都实现了,当然以上我只贴了部分代码,展现了基本的处理思路,如果想要更加详细的了解并且学习的话,可以去我的github上下载查阅。
      下面附上我的源代码链接:
      https://github.com/AndroidStudio2017/TwoGram

      如有叙述有误,欢迎指正!
      在之后我也会再更新一篇关于爬虫分享的博文和一篇分词之后2-gram的博文,有兴趣的小伙伴可以关注一下orz

2016-12-30 10:51:16 u014473682 阅读数 939
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    31035 人正在学习 去看看 张中强
   关于语音识别的基础知识和sphinx的知识,具体可以参考我的另外的博文:

语音识别的基础知识与CMUsphinx介绍:

http://blog.csdn.net/zouxy09/article/details/7941585

PocketSphinx语音识别系统的编译、安装和使用:

http://blog.csdn.net/zouxy09/article/details/7942784

PocketSphinx语音识别系统语言模型的训练和声学模型的改进:

http://blog.csdn.net/zouxy09/article/details/7949126

PocketSphinx语音识别系统声学模型的训练与使用

http://blog.csdn.net/zouxy09/article/details/7962382

 

        本文主要实现PocketSphinx语音识别系统的编程使用,主要分两个方面,一个是编程解码语音文件(主要参考CMU sphinxwikihttp://cmusphinx.sourceforge.net/wiki/),二是编程识别麦克风的语音(主要参考PocketSphinx源码包里的pocketsphinx.c文件)。对于后面加入我的人机交互系统的话,采用的是识别麦克风的语音的编程,具体使用时还需要对其进行精简。

 

一、编程解码语音文件

1、编程:

  1. #include <pocketsphinx.h>  
  2.   
  3. int main(int argc, char *argv[])  
  4. {  
  5.     ps_decoder_t *ps;  
  6.     cmd_ln_t *config;  
  7.     FILE *fh;  
  8.     char const *hyp, *uttid;  
  9.         int16 buf[512];  
  10.     int rv;  
  11.     int32 score;  
  12.   
  13.     //1、初始化:创建一个配置对象 cmd_ln_t *  
  14.     //cmd_ln_init函数第一个参数是我们需要更新的上一个配置,因为这里是初次创建,所以传入NULL;  
  15.     //第二个参数是一个定义参数的数组,如果使用的是标准配置的参数集的话可以通过调用ps_args()去获得。  
  16.     //第三个参数是是一个标志,它决定了参数的解释是否严格,如果为TRUE,那么遇到重复的或者未知的参  
  17.     //数,将会导致解释失败;  
  18.     //MODELDIR这个宏,指定了模型的路径,包括声学模型,语言模型和字典三个文件,是由gcc命令行传入,  
  19.     //我们通过pkg-config工具从PocketSphinx的配置中去获得这个modeldir变量  
  20.     config = cmd_ln_init(NULL, ps_args(), TRUE,  
  21.                  "-hmm", MODELDIR "/hmm/en_US/hub4wsj_sc_8k",  
  22.                  "-lm", MODELDIR "/lm/en/turtle.DMP",  
  23.                  "-dict", MODELDIR "/lm/en/turtle.dic",  
  24.                  NULL);  
  25.     if (config == NULL)  
  26.         return 1;  
  27.       
  28.     //2、初始化解码器(语言识别就是一个解码过程,通俗的将就是将你说的话解码成对应的文字串)  
  29.     ps = ps_init(config);  
  30.     if (ps == NULL)  
  31.         return 1;  
  32.   
  33.     //3、解码文件流  
  34.     //因为音频输入接口(麦克风)受到一些特定平台的影响,不利用我们演示,所以我们通过解码音频文件流  
  35.     //来演示PocketSphinx API的用法,goforward.raw是一个包含了一些诸如“go forward ten meters”等用来  
  36.     //控制机器人的短语(指令)的音频文件,其在test/data/goforward.raw。把它复制到当前目录  
  37.     fh = fopen("/dev/input/event14""rb");  
  38.     if (fh == NULL) {  
  39.         perror("Failed to open goforward.raw");  
  40.         return 1;  
  41.     }  
  42.       
  43.     //4、使用ps_decode_raw()进行解码  
  44.       
  45.     rv = ps_decode_raw(ps, fh, NULL, -1);  
  46.     if (rv < 0)  
  47.         return 1;  
  48.       
  49.     //5、得到解码的结果(概率最大的字串) hypothesis  
  50.     hyp = ps_get_hyp(ps, &score, &uttid);  
  51.     if (hyp == NULL)  
  52.         return 1;  
  53.     printf("Recognized: %s\n", hyp);  
  54.   
  55.     //从内存中解码音频数据  
  56.     //现在我们将再次解码相同的文件,但是使用API从内存块中解码音频数据。在这种情况下,首先我们  
  57.     //需要使用ps_start_utt()开始说话:  
  58.     fseek(fh, 0, SEEK_SET);  
  59.       
  60.     rv = ps_start_utt(ps, NULL);  
  61.     if (rv < 0)  
  62.         return 1;  
  63.         while (!feof(fh)) {  
  64.     rv = ps_start_utt(ps, NULL);  
  65.         if (rv < 0)  
  66.                 return 1;  
  67.   
  68.         printf("ready:\n");  
  69.             size_t nsamp;  
  70.             nsamp = fread(buf, 2, 512, fh);  
  71.         printf("read:\n");  
  72.             //我们将每次从文件中读取512大小的样本,使用ps_process_raw()把它们放到解码器中:  
  73.             rv = ps_process_raw(ps, buf, nsamp, FALSE, FALSE);  
  74.         printf("process:\n");  
  75.         }  
  76.         //我们需要使用ps_end_utt()去标记说话的结尾处:  
  77.         rv = ps_end_utt(ps);  
  78.     if (rv < 0)  
  79.         return 1;  
  80.           
  81.     //以相同精确的方式运行来检索假设的字符串:  
  82.     hyp = ps_get_hyp(ps, &score, &uttid);  
  83.     if (hyp == NULL)  
  84.         return 1;  
  85.     printf("Recognized: %s\n", hyp);  
  86.     }  
  87.     //6、清理工作:使用ps_free()释放使用ps_init()返回的对象,不用释放配置对象。  
  88.     fclose(fh);  
  89.         ps_free(ps);  
  90.     return 0;  
  91. }  


 

 

2、编译:

编译方法:

gcc -o test_ps test_ps.c \

    -DMODELDIR=\"`pkg-config --variable=modeldir pocketsphinx`\" \

    `pkg-config --cflags --libs pocketsphinx sphinxbase`

//gcc-D选项,指定宏定义,如-Dmacro=defn   相当于C语言中的#define macro=defn那么上面就表示在test_ps.c文件中,新加入一个宏定义:

#define MODELDIR=\"`pkg-config --variable=modeldir pocketsphinx`\"

\表示转义符,把“号转义。

         这么做是为什么呢?因为程序中需要指定MODELDIR这个变量,但是因为不同的使用者,这个变量不一样,没办法指定死一个路径,所以只能放在编译时,让用户去根据自己的情况来指定。

 

pkg-config工具可以获得一个库的编译和连接等信息;

#pkg-config --cflags --libs pocketsphinx sphinxbase

显示:

-I/usr/local/include/sphinxbase  -I/usr/local/include/pocketsphinx

-L/usr/local/lib -lpocketsphinx -lsphinxbase –lsphinxad

 

#pkg-config --variable=modeldir pocketsphinx

显示结果输出:/usr/local/share/pocketsphinx/model

二、编程解码麦克风的录音

1、编程

       麦克风录音数据的获得主要是用sphinxbase封装了alsa的接口来实现。

  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #include <sys/types.h>  
  4. #include <sys/time.h>  
  5. #include <signal.h>  
  6. #include <setjmp.h>  
  7.   
  8. #include <sphinxbase/err.h>  
  9. //generic live audio interface for recording and playback  
  10. #include <sphinxbase/ad.h>  
  11. #include <sphinxbase/cont_ad.h>  
  12.   
  13. #include "pocketsphinx.h"  
  14.   
  15. static ps_decoder_t *ps;  
  16. static cmd_ln_t *config;  
  17.   
  18. static void print_word_times(int32 start)  
  19. {  
  20.     ps_seg_t *iter = ps_seg_iter(ps, NULL);  
  21.     while (iter != NULL)   
  22.     {  
  23.         int32 sf, ef, pprob;  
  24.         float conf;  
  25.           
  26.         ps_seg_frames (iter, &sf, &ef);  
  27.         pprob = ps_seg_prob (iter, NULL, NULL, NULL);  
  28.         conf = logmath_exp(ps_get_logmath(ps), pprob);  
  29.         printf ("%s %f %f %f\n", ps_seg_word (iter), (sf + start) / 100.0, (ef + start) / 100.0, conf);  
  30.         iter = ps_seg_next (iter);  
  31.     }  
  32. }  
  33.   
  34. /* Sleep for specified msec */  
  35. static void sleep_msec(int32 ms)  
  36. {  
  37.     struct timeval tmo;  
  38.   
  39.     tmo.tv_sec = 0;  
  40.     tmo.tv_usec = ms * 1000;  
  41.   
  42.     select(0, NULL, NULL, NULL, &tmo);  
  43. }  
  44.   
  45. /* 
  46.  * Main utterance processing loop: 
  47.  *     for (;;) { 
  48.  *     wait for start of next utterance; 
  49.  *     decode utterance until silence of at least 1 sec observed; 
  50.  *     print utterance result; 
  51.  *     } 
  52.  */  
  53. static void recognize_from_microphone()  
  54. {  
  55.     ad_rec_t *ad;  
  56.     int16 adbuf[4096];  
  57.     int32 k, ts, rem;  
  58.     char const *hyp;  
  59.     char const *uttid;  
  60.     cont_ad_t *cont;  
  61.     char word[256];  
  62.   
  63.     if ((ad = ad_open_dev(cmd_ln_str_r(config, "-adcdev"),  
  64.                           (int)cmd_ln_float32_r(config, "-samprate"))) == NULL)  
  65.         E_FATAL("Failed top open audio device\n");  
  66.   
  67.     /* Initialize continuous listening module */  
  68.     if ((cont = cont_ad_init(ad, ad_read)) == NULL)  
  69.         E_FATAL("Failed to initialize voice activity detection\n");  
  70.     if (ad_start_rec(ad) < 0)  
  71.         E_FATAL("Failed to start recording\n");  
  72.     if (cont_ad_calib(cont) < 0)  
  73.         E_FATAL("Failed to calibrate voice activity detection\n");  
  74.   
  75.     for (;;) {  
  76.         /* Indicate listening for next utterance */  
  77.         printf("READY....\n");  
  78.         fflush(stdout);  
  79.         fflush(stderr);  
  80.   
  81.         /* Wait data for next utterance */  
  82.         while ((k = cont_ad_read(cont, adbuf, 4096)) == 0)  
  83.             sleep_msec(100);  
  84.   
  85.         if (k < 0)  
  86.             E_FATAL("Failed to read audio\n");  
  87.   
  88.         /* 
  89.          * Non-zero amount of data received; start recognition of new utterance. 
  90.          * NULL argument to uttproc_begin_utt => automatic generation of utterance-id. 
  91.          */  
  92.         if (ps_start_utt(ps, NULL) < 0)  
  93.             E_FATAL("Failed to start utterance\n");  
  94.         ps_process_raw(ps, adbuf, k, FALSE, FALSE);  
  95.         printf("Listening...\n");  
  96.         fflush(stdout);  
  97.   
  98.         /* Note timestamp for this first block of data */  
  99.         ts = cont->read_ts;  
  100.   
  101.         /* Decode utterance until end (marked by a "long" silence, >1sec) */  
  102.         for (;;) {  
  103.             /* Read non-silence audio data, if any, from continuous listening module */  
  104.             if ((k = cont_ad_read(cont, adbuf, 4096)) < 0)  
  105.                 E_FATAL("Failed to read audio\n");  
  106.             if (k == 0) {  
  107.                 /* 
  108.                  * No speech data available; check current timestamp with most recent 
  109.                  * speech to see if more than 1 sec elapsed.  If so, end of utterance. 
  110.                  */  
  111.                 if ((cont->read_ts - ts) > DEFAULT_SAMPLES_PER_SEC)  
  112.                     break;  
  113.             }  
  114.             else {  
  115.                 /* New speech data received; note current timestamp */  
  116.                 ts = cont->read_ts;  
  117.             }  
  118.   
  119.             /* 
  120.              * Decode whatever data was read above. 
  121.              */  
  122.             rem = ps_process_raw(ps, adbuf, k, FALSE, FALSE);  
  123.   
  124.             /* If no work to be done, sleep a bit */  
  125.             if ((rem == 0) && (k == 0))  
  126.                 sleep_msec(20);  
  127.         }  
  128.   
  129.         /* 
  130.          * Utterance ended; flush any accumulated, unprocessed A/D data and stop 
  131.          * listening until current utterance completely decoded 
  132.          */  
  133.         ad_stop_rec(ad);  
  134.         while (ad_read(ad, adbuf, 4096) >= 0);  
  135.         cont_ad_reset(cont);  
  136.   
  137.         printf("Stopped listening, please wait...\n");  
  138.         fflush(stdout);  
  139.         /* Finish decoding, obtain and print result */  
  140.         ps_end_utt(ps);  
  141.         hyp = ps_get_hyp(ps, NULL, &uttid);  
  142.         printf("%s: %s\n", uttid, hyp);  
  143.         fflush(stdout);  
  144.   
  145.         /* Exit if the first word spoken was GOODBYE */  
  146.         if (hyp) {  
  147.             sscanf(hyp, "%s", word);  
  148.             if (strcmp(word, "goodbye") == 0)  
  149.                 break;  
  150.         }  
  151.   
  152.         /* Resume A/D recording for next utterance */  
  153.         if (ad_start_rec(ad) < 0)  
  154.             E_FATAL("Failed to start recording\n");  
  155.     }  
  156.   
  157.     cont_ad_close(cont);  
  158.     ad_close(ad);  
  159. }  
  160.   
  161. static jmp_buf jbuf;  
  162. static void sighandler(int signo)  
  163. {  
  164.     longjmp(jbuf, 1);  
  165. }  
  166.   
  167. int main(int argc, char *argv[])  
  168. {  
  169.       
  170.     config = cmd_ln_init(NULL, ps_args(), TRUE,  
  171.                  "-hmm", MODELDIR "/hmm/en_US/hub4wsj_sc_8k",  
  172.                  "-lm", MODELDIR "/lm/en/turtle.DMP",  
  173.                  "-dict", MODELDIR "/lm/en/turtle.dic",  
  174.                  NULL);  
  175.     if (config == NULL)  
  176.         return 1;  
  177.       
  178.     ps = ps_init(config);  
  179.     if (ps == NULL)  
  180.         return 1;  
  181.   
  182.     signal(SIGINT, &sighandler);  
  183.     if (setjmp(jbuf) == 0)   
  184.         recognize_from_microphone();  
  185.       
  186.         ps_free(ps);  
  187.     return 0;  
  188. }  

 

2、编译

1.2一样。

 

       至于说后面把PocketSphinx语音识别系统加入我的人机交互系统这个阶段,因为感觉这个系统本身的识别率不是很高,自己做了适应和重新训练声学和语言模型后,提升还是有限,暂时实用性还不是很强,所以暂时搁置下,看能不能通过其他方法去改进目前的状态。希望有牛人指导下。另外,由于开学了,需要上课,所以后续的进程可能会稍微减慢,不过依然期待各位多多交流!呵呵

2019-12-04 19:03:26 adrry01 阅读数 32
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    31035 人正在学习 去看看 张中强

Tensorflow是Google推出的机器学习开源神器,对Python有着良好的语言支持,支持CPU,GPU和Google TPU等硬件,并且已经拥有了各种各样的模型和算法。目前,Tensorflow已被广泛应用于文本处理,语音识别和图像识别等多项机器学习和深度学习领域。

基础框架

分为三层:应用层、接口层和核心层
应用层

提供了机器学习相关的训练库、预测库和针对Python、C++和Java等变成语言的编程环境,类似于web系统的前端,主要实现了对计算图的构造。

接口层

对Tensorflow功能模块的封装,便于其它语言平台的调用。

核心层

最重要的部分,包括设备层、网络层、数据操作层和图计算层,执行应用层的计算。

1.设备层

包括Tensorflow在不同硬件设备上的实现,主要支持CPU、GPU和Mobile等设备,在不同硬件设备上实现计算命令的转换,给上层提供统一的接口,实现程序的跨平台功能。

2.网络层

网络层主要包括RPC和RDMA通信协议,实现不同设备之间的数据传输和更新,这些协议都会在分布式计算中用到。

3.数据操作层

以tensor为处理对象,实现tensor的各种操作和计算。

4.图计算层

包括分布式计算图和本地计算图的实现,实现图的创建、编译、优化和执行等。

设计理念

可以将Tensorflow理解为一张计算图中“张量的流动”,其中,Tensor(张量)代表了计算图中的边,Flow(流动)代表了计算图中节点所做的操作而形成的数据流动。

其设计理念是以数据流为核心,当构建相应的机器学习模型后,使用训练数据在模型中进行数据流动,同时将结果以反向传播的方式反馈给模型中的参数,以进行调参,使用调整后的参数对训练数据再次进行迭代计算。

编程特点

有两个编程特点:

图的定义和图的运行完全分开

在tensorflow中,需要预先定义各种变量,建立相关的数据流图,在数据流图中创建各种变量之间的计算关系,完成图的定义,需要把运算的输入数据放进去后,才会形成输出值。

图的计算在会话中执行

tensorflow的相关计算在图中进行定义,而图的具体运行坏境在会话(session)中,开启会话后,才能开始计算,关闭会话就不能再进行计算了。

举个例子:
在这里插入图片描述
可以看出,在图创建后,并在会话中执行数据计算,最终输出结果。

设计的好处就是:学习的过程中,消耗最多的是对数据的训练,这样设计的话,当进行计算时,图已经确定,计算就只剩下一个不断迭代的过程。

基本概念

Tensor

张量,是tensorflow中最主要的数据结构,张量用于在计算图中进行数据传递,创建了张量后,需要将其赋值给一个变量或占位符,之后才会将该张量添加到计算图中。

session

会话,是Tensorflow中计算图的具体执行者,与图进行实际的交互。一个会话中可以有多个图,会话的主要目的是将训练数据添加到图中进行计算,也可以修改图的结构。

调用模式推荐使用with语句:
在这里插入图片描述
变量,表示图中的各个计算参数,通过调整这些变量的状态来优化机器学习算法。创建变量应使用tf.Variable(),通过输入一个张量,返回一个变量,变量声明后需进行初始化才能使用。

举例说明:在这里插入图片描述
Placeholder

占位符,用于表示输入输出数据的格式,声明了数据位置,允许传入指定类型和形状的数据,通过会话中的feed_dict参数获取数据,在计算图运行时使用获取的数据进行计算,计算完毕后获取的数据就会消失。

举例说明:
在这里插入图片描述
Operation

操作,是图中的节点,输入输出都是Tensor,作用是完成各种操作,包括:

数学运算:add, sub, mul, div, exp …

数组运算:concat, slice, split, rank …

矩阵运算:matmul, matrixinverse …

神经网络构建:softmax, sigmoid, relu …

检查点:save, restore …

队列和同步:enqueue, dequeue, mutexacquire, mutexrelease …

张量控制:merge, switch, enter, leave …

Queue

队列,图中有状态的节点。包含入列(endqueue)和出列(dequeue)两个操作,入列返回计算图中的一个操作节点,出列返回一个tensor值。

其中,队列有两种:

  1. FIFOQueue

按入列顺序出列的队列,在需要读入的训练样本有序时使用。举个例子:
在这里插入图片描述
2. RandomShuffleQueue

以随机顺序出列的队列,读入的训练样本无序时使用。举个例子:
在这里插入图片描述

2018-12-21 21:03:09 lylclz 阅读数 2038
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    31035 人正在学习 去看看 张中强

git地址:https://github.com/chenlinzhong/face-login

本文主要介绍了系统涉及的人脸检测与识别的详细方法,该系统基于python2.7.10/opencv2/tensorflow1.7.0环境,实现了从摄像头读取视频,检测人脸,识别人脸的功能
由于模型文件过大,git无法上传,整个项目放在百度云盘,地址:https://pan.baidu.com/s/1TaalpwQwPTqlCIfXInS_LA

人脸识别是计算机视觉研究领域的一个热点。目前,在实验室环境下,许多人脸识别已经赶上(超过)人工识别精度(准确率:0.9427~0.9920),比如face++,DeepID3,FaceNet等(详情可以参考:基于深度学习的人脸识别技术综述)。但是,由于光线,角度,表情,年龄等多种因素,导致人脸识别技术无法在现实生活中广泛应用。本文基于python/opencv/tensorflow环境,采用FaceNet(LFW:0.9963 )为基础来构建实时人脸检测与识别系统,探索人脸识别系统在现实应用中的难点。下文主要内容如下 :

  1. 利用htm5 video标签打开摄像头采集头像并使用jquery.faceDeaction组件来粗略检测人脸
  2. 将人脸图像上传到服务器,采用mtcnn检测人脸
  3. 利用opencv的仿射变换对人脸进行对齐,保存对齐后的人脸
  4. 采用预训练的facenet对检测的人脸进行embedding,embedding成512维度的特征;
  5. 对人脸embedding特征创建高效的annoy索引进行人脸检测

人脸采集

采用html5 video标签可以很方便的实现从摄像头读取视频帧,下文代码实现了从摄像头读取视频帧,faceDection识别人脸后截取图像上传到服务器功能
在html文件中添加video,canvas标签

<div class="booth">
    <video id="video" width="400" height="300" muted class="abs" ></video>
    <canvas id="canvas" width="400" height="300"></canvas>
  </div>

打开网络摄像头

var video = document.getElementById('video'),
var vendorUrl = window.URL || window.webkitURL;
//媒体对象
navigator.getMedia = navigator.getUserMedia || navagator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
navigator.getMedia({video: true, //使用摄像头对象audio: false  //不适用音频}, function(strem){
    video.src = vendorUrl.createObjectURL(strem);
    video.play();
});

利用jquery的facetDection组件检测人脸

$(’#canvas’).faceDetection()

检测出人连脸的话截图,并把图片转换为base64的格式,方便上传

context.drawImage(video, 0, 0, video.width, video.height);
var base64 = canvas.toDataURL('images/png');

将base64格式的图片上传到服务器

//上传人脸图片
function upload(base64) {
  $.ajax({
      "type":"POST",
      "url":"/upload.php",
      "data":{'img':base64},
      'dataType':'json',
      beforeSend:function(){},
      success:function(result){
          console.log(result)
          img_path = result.data.file_path
      }
  });
}

图片服务器接受代码,php语言实现

function base64_image_content($base64_image_content,$path){
    //匹配出图片的格式
    if (preg_match('/^(data:\s*image\/(\w+);base64,)/', $base64_image_content, $result)){
        $type = $result[2];
        $new_file = $path."/";
        if(!file_exists($new_file)){
            //检查是否有该文件夹,如果没有就创建,并给予最高权限
            mkdir($new_file, 0700,true);
        }
        $new_file = $new_file.time().".{$type}";
        if (file_put_contents($new_file, base64_decode(str_replace($result[1], '', $base64_image_content)))){
            return $new_file;
        }else{
            return false;
        }
    }else{
        return false;
    }
}

人脸检测

人脸检测方法有许多,比如opencv自带的人脸Haar特征分类器和dlib人脸检测方法等。
对于opencv的人脸检测方法,有点是简单,快速;存在的问题是人脸检测效果不好。正面/垂直/光线较好的人脸,该方法可以检测出来,而侧面/歪斜/光线不好的人脸,无法检测。因此,该方法不适合现场应用。对于dlib人脸检测方法 ,效果好于opencv的方法,但是检测力度也难以达到现场应用标准。
本文中,我们采用了基于深度学习方法的mtcnn人脸检测系统(mtcnn:Joint Face Detection and Alignment using Multi-task Cascaded Convolutional Neural Networks)。mtcnn人脸检测方法对自然环境中光线,角度和人脸表情变化更具有鲁棒性,人脸检测效果更好;同时,内存消耗不大,可以实现实时人脸检测。本文中采用mtcnn是基于python和tensorflow的实现(代码来自于davidsandberg,caffe实现代码参见:kpzhang93)

model= os.path.abspath(face_comm.get_conf('mtcnn','model'))
class Detect:
    def __init__(self):
        self.detector = MtcnnDetector(model_folder=model, ctx=mx.cpu(0), num_worker=4, accurate_landmark=False)
    def detect_face(self,image):
        img = cv2.imread(image)
        results =self.detector.detect_face(img)
        boxes=[]
        key_points = []
        if results is not None:  
            #box框
            boxes=results[0]
            #人脸5个关键点
            points = results[1]
            for i in results[0]:
                faceKeyPoint = []
                for p in points:
                    for i in range(5):
                        faceKeyPoint.append([p[i], p[i + 5]])
                key_points.append(faceKeyPoint)
        return {"boxes":boxes,"face_key_point":key_points}

具体代码参考fcce_detect.py

人脸对齐

有时候我们截取的人脸了头像可能是歪的,为了提升检测的质量,需要把人脸校正到同一个标准位置,这个位置是我们定义的,假设我们设定的标准检测头像是这样的

在这里插入图片描述

假设眼睛,鼻子三个点的坐标分别是a(10,30) b(20,30) c(15,45),具体设置可参看config.ini文件alignment块配置项

采用opencv仿射变换进行对齐,获取仿射变换矩阵

dst_point=【a,b,c】
tranform = cv2.getAffineTransform(source_point, dst_point)

仿射变换:

img_new = cv2.warpAffine(img, tranform, imagesize)

具体代码参考face_alignment.py文件

产生特征

对齐得到后的头像,放入采用预训练的facenet对检测的人脸进行embedding,embedding成512维度的特征,以(id,vector)的形式保存在lmdb文件中

 facenet.load_model(facenet_model_checkpoint)
 images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0")
 embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0")
 phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")

 face=self.dectection.find_faces(image)
 prewhiten_face = facenet.prewhiten(face.image)
 # Run forward pass to calculate embeddings
 feed_dict = {images_placeholder: [prewhiten_face], phase_train_placeholder: False}
 return self.sess.run(embeddings, feed_dict=feed_dict)[0]

具体代码可参看face_encoder.py

人脸特征索引:

人脸识别的时候不能对每一个人脸都进行比较,太慢了,相同的人得到的特征索引都是比较类似,可以采用KNN分类算法去识别,这里采用是更高效annoy算法对人脸特征创建索引,annoy索引算法的有个假设就是,每个人脸特征可以看做是在高维空间的一个点,如果两个很接近(相识),任何超平面
都无法把他们分开,也就是说如果空间的点很接近,用超平面去分隔,相似的点一定会分在同一个平面空间(具体参看:https://github.com/spotify/annoy)

#人脸特征先存储在lmdb文件中格式(id,vector),所以这里从lmdb文件中加载
lmdb_file = self.lmdb_file
if os.path.isdir(lmdb_file):
    evn = lmdb.open(lmdb_file)
    wfp = evn.begin()
    annoy = AnnoyIndex(self.f)
    for key, value in wfp.cursor():
        key = int(key)
        value = face_comm.str_to_embed(value)
        annoy.add_item(key,value)

    annoy.build(self.num_trees)
    annoy.save(self.annoy_index_path)

具体代码可参看face_annoy.py

人脸识别

经过上面三个步骤后,得到人脸特征,在索引中查询最近几个点并就按欧式距离,如果距离小于0.6(更据实际情况设置的阈值)则认为是同一个人,然后根据id在数据库查找到对应人的信息即可

#根据人脸特征找到相似的
def query_vector(self,face_vector):
    n=int(face_comm.get_conf('annoy','num_nn_nearst'))
    return self.annoy.get_nns_by_vector(face_vector,n,include_distances=True)

具体代码可参看face_annoy.py

安装部署

系统采用有两个模块组成:

  • face_web:提供用户注册登录,人脸采集,php语言实现
  • face_server: 提供人脸检测,裁剪,对齐,识别功能,python语言实现

模块间采用socket方式通信通信格式为: length+content

face_server相关的配置在config.ini文件中

1.使用镜像

  • face_serverdocker镜像: shareclz/python2.7.10-face-image
  • face_web镜像: skiychan/nginx-php7

假设项目路径为/data1/face-login

2.安装face_server容器

docker run -it --name=face_server --net=host  -v /data1:/data1  shareclz/python2.7.10-face-image /bin/bash
cd /data1/face-login
python face_server.py

3.安装face_web容器

docker run -it --name=face_web --net=host  -v /data1:/data1  skiychan/nginx-php7 /bin/bash
cd /data1/face-login;
php -S 0.0.0.0:9988 -t ./web/ 

最终效果:

face_server加载mtcnn模型和facenet模型后等待人脸请求
在这里插入图片描述

未注册识别失败
在这里插入图片描述

人脸注册
在这里插入图片描述

注册后登录成功
在这里插入图片描述

参考

https://zhuanlan.zhihu.com/p/25025596

https://github.com/spotify/annoy

https://blog.csdn.net/just_sort/article/details/79337526

https://blog.csdn.net/oTengYue/article/details/79278572

没有更多推荐了,返回首页