机器人的语音交互——基于讯飞和seq2seq问答

目的:

        实现可以语音交流的问答机器人。

步骤:

         1. 讯飞语音识别(输入)

         2. seq2seq 问答

         3. 讯飞语音合成(输出)

         4. 整合

1. 讯飞语音识别(输入)  

思想:从麦克风输入,通过科大讯飞的在线语音识别SDK(需要联网),将语音输入保存为txt文件。

首先,需要一个科大讯飞的帐号,然后需要创建应用(可能需要等待一段时间审核),选择Linux应用平台,不创建应用是不能下载sdk的,有了自己的应用后进行SDK下载。

机器人的语音交互——基于讯飞和seq2seq问答_第1张图片

如图所示,我们选择语音听写和在线语音合成这两个免费的项目,然后将SDK下载下来。 一个文件名以Linux开头,包含了专属ID的文件夹,本人将其改名成了xunfei。文件夹下有bin、doc、include、libs、samples五个子文件夹。

我们主要看samples文件夹,里面也有五个文件夹,我们只需要iat_online_record_sample(在线语言识别)和tts_online_sample(在线语音合成)。

语言识别测试:

可以先测试一下,64位ubuntu系统,在iat_online_record_sample 文件夹下,输入:

source 64bit_make.sh //运行64bit_make.sh
cd ../../bin  //切换到bin目录下
./iat_online_record_sample  //运行iat_online_record_sample可执行文件

需要联网,我们选择不上传用户词汇表,和从micphone输入,我们就可以对着电脑说一段话,15秒后会打印出语音识别结果。

修改语言识别保存到txt:

我们回到iat_online_record_sample文件夹下,打开iat_online_record_sample.c文件,注释掉 是否上传用户词表和录音文件与麦克风输入的选择(aud_src=1 直接选择了麦克风输入),appid是自己的appid(原来的iat_online_record_sample.c中已经写好了),main函数改成下面的样子:

/* main thread: start/stop record ; query the result of recgonization.
 * record thread: record callback(data write)
 * helper thread: ui(keystroke detection)
 */
int main(int argc, char* argv[])
{
	int ret = MSP_SUCCESS;
	int upload_on =	1; /* whether upload the user word */
	/* login params, please do keep the appid correct */
	const char* login_params = "appid = 你自己的appid, work_dir = .";
	int aud_src = 0; /* from mic or file */

	/*
	* See "iFlytek MSC Reference Manual"
	*/
	const char* session_begin_params =
		"sub = iat, domain = iat, language = zh_cn, "
		"accent = mandarin, sample_rate = 16000, "
		"result_type = plain, result_encoding = utf8";

	/* Login first. the 1st arg is username, the 2nd arg is password
	 * just set them as NULL. the 3rd arg is login paramertes 
	 * */
	ret = MSPLogin(NULL, NULL, login_params);
	if (MSP_SUCCESS != ret)	{
		printf("MSPLogin failed , Error code %d.\n",ret);
		goto exit; // login fail, exit the program
	}
	
	/*
	printf("Want to upload the user words ? \n0: No.\n1: Yes\n");
	scanf("%d", &upload_on);
	if (upload_on)
	{
		printf("Uploading the user words ...\n");
		ret = upload_userwords();
		if (MSP_SUCCESS != ret)
			goto exit;	
		printf("Uploaded successfully\n");
	}
	*/

	//printf("Where the audio comes from?\n"
	//		"0: From a audio file.\n1: From microphone.\n");
	//scanf("%d", &aud_src);
	aud_src=1; //从microphone输入
	if(aud_src != 0) {
		printf("Demo recognizing the speech from microphone\n");
		printf("Speak in 15 seconds\n");

		demo_mic(session_begin_params);

		printf("15 sec passed\n");
	} else {
		printf("Demo recgonizing the speech from a recorded audio file\n");
		demo_file("wav/iflytek02.wav", session_begin_params); 
	}
exit:
	MSPLogout(); // Logout...

	return 0;
}

为了让语音识别能保存到txt中,showresult函数修改为:

static void show_result(char *string, char is_over)
{
	printf("\rResult: [ %s ]", string);
	if(is_over)
		putchar('\n');
	//2019.1.12 
	FILE *fp;
	fp = fopen("recognition.txt", "w");
	fprintf(fp, string);
	fclose(fp);

}

另外,demo_mic函数中可以修改说话的时间,默认十五秒。

修改完成后,再一次生成可执行文件,然后测试一下:

source 64bit_make.sh //运行64bit_make.sh
cd ../../bin  //切换到bin目录下
./iat_online_record_sample  //运行iat_online_record_sample可执行文件

 识别效果如图所示:

 机器人的语音交互——基于讯飞和seq2seq问答_第2张图片

同时,在讯飞的bin下会生成一个recognition.txt,保存了我们语音输入的话。

2. seq2seq 问答

思想:读取语音识别得到的txt,通过训练好的seq2seq模型,得出对这句话的回答。

seq2seq 问答机器人来源于开源项目 dynamic-seq2seq-master:https://github.com/yanwii/dynamic-seq2seq

小黄鸡的对话语料库,本身就答非所问地,训练完结果更加答非所问。

环境为tensorflow1.4, 修改后可以在python3下运行,print加括号,以及在utils.py中将

self.data = zip(self.Q, self.A) 修改为 self.data = list(zip(self.Q, self.A))

用法:

python3 prepare_dialog.py 10000   //挑选对话语料中的前10000个为训练样本,全部训练样本太大。
python3 seq2seq.py retrain   //重新训练。

训练中可能遇到错误

Invalid argument: logits and labels must have the same first dimension, got logits shape [20,9003] and labels shape [36]

大致意思是计算logits[20,9003]与标签label[36] 不对应,经过仔细看,发现在dynamic_seq2seq_model.py中,_init_decoder函数下,解码最多只能比编码长度大20倍。

maximum_iterations = tf.round(tf.reduce_max(self.encoder_inputs_length) * 20)

 改为如下,解码比编码大40倍即可。

maximum_iterations = tf.round(tf.reduce_max(self.encoder_inputs_length) * 40)

对于训练过程,如果希望快速收敛,可以先将学习率调高训练,然后训练到一半,调小学习率再进行训练。同样是在dynamic_seq2seq_model.py,_init_optimizer函数中。

optimizer = tf.train.GradientDescentOptimizer(1)  #初始训练时候学习率设置为1
#当损失经过明显下降后,ctrl+c取消,然后调整学习率到0.1 python3 seq2seq.py train 继续训练
#optimizer = tf.train.GradientDescentOptimizer(0.1)  

#可以用不同的优化器,例如
#optimizer = tf.train.AdamOptimizer(1)   

测试:

主函数中当运行附带参数为infer时候,会打印出seq.predict(' xxxx ')的回答,‘xxxx’就是我们说的话,以后通过读取语音识别的txt文件获取。我们运行

python3 seq2seq.py infer

即可以得到回答。

修改从txt来到txt去:

将主函数改成如下:

if __name__== '__main__':
    seq = Seq2seq()
    r = open('xunfei/recognition.txt')  #讯飞的目录,我这个是相对路径
    sequence=r.read()
    answer=seq.predict(sequence)
    w = open('xunfei/response.txt','w')  #讯飞的目录
    w.write(answer)

 3. 讯飞语音合成(输出)

思想:从txt中读取文本,语音合成并输出

有了用讯飞语言识别的经验,语音合成也是类似。

切换到xunfei/sample/tts_online_sample 目录下,将主函数改为:

int main(int argc, char* argv[])
{
	int         ret                  = MSP_SUCCESS;
	const char* login_params         = "appid = ‘自己的appid’, work_dir = .";//登录参数,appid与msc库绑定,请勿随意改动
	/*
	* rdn:           合成音频数字发音方式
	* volume:        合成音频的音量
	* pitch:         合成音频的音调
	* speed:         合成音频对应的语速
	* voice_name:    合成发音人
	* sample_rate:   合成音频采样率
	* text_encoding: 合成文本编码格式
	*
	*/
	const char* session_begin_params = "voice_name = vixk, text_encoding = utf8, sample_rate = 16000, speed = 50, volume = 50, pitch = 50, rdn = 2";
	const char* filename             = "tts_sample.wav"; //合成的语音文件名称
	
	// 19.1.11  
	char         read_result[100] = "";
	FILE *fp = NULL;
	fp = fopen("response.txt","rb");
	fscanf(fp , "%s" , read_result);
	fclose(fp);
	// 19.1.11 

	const char* text                 =  read_result; //合成文本
	
	/* 用户登录 */
	ret = MSPLogin(NULL, NULL, login_params);//第一个参数是用户名,第二个参数是密码,第三个参数是登录参数,用户名和密码可在http://www.xfyun.cn注册获取
	if (MSP_SUCCESS != ret)
	{
		printf("MSPLogin failed, error code: %d.\n", ret);
		goto exit ;//登录失败,退出登录
	}
	printf("\n###########################################################################\n");
	printf("## 语音合成(Text To Speech,TTS)技术能够自动将任意文字实时转换为连续的 ##\n");
	printf("## 自然语音,是一种能够在任何时间、任何地点,向任何人提供语音信息服务的  ##\n");
	printf("## 高效便捷手段,非常符合信息时代海量数据、动态更新和个性化查询的需求。  ##\n");
	printf("###########################################################################\n\n");
	/* 文本合成 */
	printf("开始合成 ...\n");
	ret = text_to_speech(text, filename, session_begin_params);
	if (MSP_SUCCESS != ret)
	{
		printf("text_to_speech failed, error code: %d.\n", ret);
	}
	printf("合成完毕\n");

exit:
	//printf("按任意键退出 ...\n");
	//getchar();
	MSPLogout(); //退出登录

	return 0;
}

其实就加了一段:

	char         read_result[100] = "";
	FILE *fp = NULL;
	fp = fopen("response.txt","rb");
	fscanf(fp , "%s" , read_result);
	fclose(fp);

用于从文本中读入字符串转化为音频信号。

source 64bit_make.sh 

会在bin 文件夹下生成一个tts_online_sample的可执行文件,运行这个程序会将response.txt 中的话变成一个tts_sample.wav的音频文件。

4. 整合和音频播放

涉及到相对目录 ,我目录列表为如下,把讯飞的sdk整个移动到dynamic-seq2seq-master 文件夹下了。

├── action.py
├── chat.py
├── _config.yml
├── data
├── dialog
├── dynamic_seq2seq_model.py
├── model
├── prepare_dialog.py
├── preprocess.py
├── README.md
├── seq2seq.py
├── test.py
├── utils.py
└── xunfei

新建了chat.py 

#coding=utf-8

import os
import pyaudio
import wave
from seq2seq import Seq2seq


def play_wav(file):
    # 定义数据流块
    chunk = 1024

    # 只读方式打开wav文件
    f = wave.open(file, "rb")

    p = pyaudio.PyAudio()

    # 打开数据流
    stream = p.open(format=p.get_format_from_width(f.getsampwidth()),
                    channels=f.getnchannels(),
                    rate=f.getframerate(),
                    output=True)

    # 读取数据
    data = f.readframes(chunk)

    # 播放
    while data != b"":
        stream.write(data)
        data = f.readframes(chunk)

    # 停止数据流
    stream.stop_stream()
    stream.close()

    # 关闭 PyAudio
    p.terminate()

if __name__ == '__main__':

    ret = os.system('xunfei/bin/iat_online_record_sample ')
    seq = Seq2seq()
    with open('recognition.txt', 'r') as f:
        sequence = f.read()
    answer = seq.predict(sequence)
    with open('response.txt', 'w') as f:
        f.write(answer[:-7])
    ret = os.system('xunfei/bin/tts_online_sample')
    play_wav('tts_sample.wav')

play_wav用于播放wav文件

f.write(answer)     写到txt里是  ——  我是人__EOS__ ,讯飞语音合成会把下划线读出来。。

f.write(answer[:-7])    写到txt里是  ——  我是人 ,这样就不会读下划线下划线了。

总结:

讯飞语音是真的强,但是相比之下,seq2seq 的问答系统就没那么好了,基本上处于答非所问的阶段。

后续:

寻找更好的语料库,做成ROS包,用于服务机器人。

你可能感兴趣的:(人工智障)