Chinese (Simplified) (中文(简体)) translation by Zhang Xiang Liang (you can also view the original English article)
几十年来,不管是科学爱好者,程序员还是AI研究人员,一直梦寐以求实现一个真正理解自然语言的APP。今天,感谢机器学习技术的巨大进步,梦想终于照进现实。 而且,基于云的服务(如Google云机器学习)已经把这些技术免费提供给每个人使用了。
在本教程中,你将学习如何使用Google云机器学习平台提供的两种功能强大的自然语言API:Cloud Speech API和Cloud Natural Language API。使用它们,你可以创建处理各种语言的语音APP。
准备工作
你需要的东西:
- Android Studio 2.2或更高版本
- 一个谷歌云平台帐户
- 运行Android 4.4或更高版本的设备
1.为什么要使用这些API?
可以处理语音的APP必须具有以下功能:
- 它必须能够从原始音频数据中提取单个单词。
- 它必须能够对提取的单词之间的语法关系作出有根据的猜测。
云语音和云自然语言API能够让你在几分钟内将上述功能添加到你的Android APP中。
云语音API作为最先进的语音识别器,可以准确地转录超过80种语言的语音。它可以强大到处理区域口音和嘈杂条件下的语音。
云自然语言API是一种语言处理系统,以近人类的精确度来确定句子中词语的角色。它目前支持十种语言,并提供实体和情感分析。
2.启用API
在使用语音和自然语言API之前,你必须在Google Cloud 控制台中启用它们。所以登录到控制台,进入API Manager > Library。

要启用Speech API,请点击Google Cloud Machine Learning部分中的Speech API链接。在接下来打开的页面中,按Enable按钮。

点击浏览器的返回按钮返回上一页。
这一次,通过点击自然语言API链接并点击下一页上的Enable按钮启用自然语言API。

你还需要一个API密钥来与API进行交互。如果你还没有,请打开Credentials选项卡,点击Create credentials按钮,然后选择“ API”键。
现在,你会看到一个显示API密钥的弹出窗口。记下这个密钥,以便以后使用。

3.配置项目
这两个API都是基于JSON的,具有REST端点,你可以直接使用任何网络库进行交互。 然而,使用可用的Google API Client库编写更易读的代码,这样你就可以节省大量时间。打开app module级的build.gradle
文件,添加以下compile
依赖项:
compile 'com.google.api-client:google-api-client-android:1.22.0' compile 'com.google.apis:google-api-services-speech:v1beta1-rev336-1.22.0' compile 'com.google.apis:google-api-services-language:v1beta2-rev6-1.22.0' compile 'com.google.code.findbugs:jsr305:2.0.1'
此外,我们将在本教程中执行一些文件I/O
操作。为了简化,添加Commons IO库的依赖关系。
compile 'commons-io:commons-io:2.5'
最后,不要忘记在AndroidManifest.xml文件中申请INTERNET
权限。否则APP将无法连接到Google服务器。
<uses-permission android:name="android.permission.INTERNET"/>
4.使用Cloud Speech API
不用说,Cloud Speech API会将音频数据作为其输入之一。因此,我们现在要创建一个可以转录音频文件的Android APP。
为了简单起见,我们只会转录FLAC文件,FLAC文件使用免费无损音频编解码器格式。你设备上可能已经有这样的文件。如果没有,建议你从维基共享资源中下载。
步骤1:创建布局
我们APP的布局有一个Button
控件,用户点击后会显示文件选择器,然后浏览并选择设备上可用的音频文件。
布局还有一个TextView
控件来显示所选音频文件的文本。代码如下:
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" android:id="@+id/speech_to_text_result" android:textSize="18sp" android:layout_alignParentTop="true"/> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:id="@+id/browse_button" android:text="Browse"/>
步骤2:创建文件选择器
当用户点击我们在上一步中创建的按钮时,会显示文件选择器界面,所以要设置OnClickListener
事件。在此操作之前,请确保已经使用findViewById
()方法初始化按钮了。
Button browseButton = (Button)findViewById(R.id.browse_button); browseButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // More code here } });
使用Android的存储访问框架(可运行在API等级19或更高版本的设备上)很轻松创建文件选择器。你只需创建一个ACTION_GET_CONTENT
动作的intent,并传递给 startActivityForResult
()方法。 或者,你可以通过给Intent
对象的setType
()方法传递MIME参数来限制文件选择程序仅显示FLAC文件。
Intent filePicker = new Intent(Intent.ACTION_GET_CONTENT); filePicker.setType("audio/flac"); startActivityForResult(filePicker, 1);
文件选择器输出的是另一个Intent
对象,该对象包含用户选择的文件URI。为了能够访问,你必须重写Activity
类的onActivityResult
()方法。
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if(resultCode == RESULT_OK) { final Uri soundUri = data.getData(); // More code here } }
步骤3:编码文件
Cloud Speech API期望音频数据采用Base64字符串的形式。要生成这样的字符串,可以将用户选择的文件内容读取到byte
数组中,然后传递给Google API Client库提供的encodeBase64String
()方法。
然而,你当前只有所选文件的URI,而不是文件的绝对路径。这意味着,你必须先解析URI才能读取文件。 将该URI传递给activity的内容解析者提供的openInputStream
()方法。 访问文件的输入流后,你可以简单地将其传递给IOUtils
类的 toByteArray
()方法,该方法将其转换为字节数组。代码如下:
AsyncTask.execute(new Runnable() { @Override public void run() { InputStream stream = getContentResolver() .openInputStream(soundUri); byte[] audioData = IOUtils.toByteArray(stream); stream.close(); String base64EncodedData = Base64.encodeBase64String(audioData); // More code here } }
如上面的代码所示,我们使用一个新的线程来运行所有的I/O操作。这样做是为了防止阻塞UI。
步骤4:播放文件
在我看来,转录的过程中就播放被转录的声音是个好主意。这实现很简单,却改善了用户体验。
使用MediaPlayer
类播放声音文件。一旦使用setDataSource
()方法指向文件的URI ,必须调用prepare
()方法来同步准备播放器。 当播放器准备就绪时,就可以调用start
()方法开始播放文件。
此外,一旦文件播放完,必须记住释放播放器的资源。为此,请分配一个OnCompletionListener
对象并调用其release
()方法。代码如下:
MediaPlayer player = new MediaPlayer(); player.setDataSource(MainActivity.this, soundUri); player.prepare(); player.start(); // Release the player player.setOnCompletionListener( new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mediaPlayer) { mediaPlayer.release(); } });
步骤5:同步转录文件
现在,将Base64编码的音频数据发送到Cloud Speech API进行转录。但首先,我建议将之前生成的API密钥存储为Activity
类的成员变量。
private final String CLOUD_API_KEY = "ABCDEF1234567890";
为了能够与Cloud Speech
API通信,必须用Speech.Builder
实例创建一个Speech对象。需要为构造方法传递HTTP传输和JSON工厂这两个参数。 另外,为了确保在API的每个HTTP请求中都包含了API密钥,必须将SpeechRequestInitializer
对象与构建器关联并将API密钥传递给过去。
以下代码使用AndroidJsonFactory
类作为JSON工厂创建Speech
对象,用NetHttpTransport
类作为HTTP传输:
Speech speechService = new Speech.Builder( AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null ).setSpeechRequestInitializer( new SpeechRequestInitializer(CLOUD_API_KEY)) .build();
Cloud Speech API必须被告知音频文件包含哪种语言。你可以通过创建RecognitionConfig
对象并调用其setLanguageCode
() 方法来实现。以下是将其配置为仅转录美式英文的方法:
RecognitionConfig recognitionConfig = new RecognitionConfig(); recognitionConfig.setLanguageCode("en-US");
此外,Base64编码的字符串必须包装在一个RecognitionAudio
对象中才能被API使用。
RecognitionAudio recognitionAudio = new RecognitionAudio(); recognitionAudio.setContent(base64EncodedData);
接下来,必须创建SyncRecognizeRequest
对象才能使用RecognitionConfig
和RecognitionAudio
对象。顾名思义,它用来创建一个HTTP请求,以便同步转录音频数据。 创建对象后,可以将其作为参数传递给syncrecognize
()方法,并调用Speech.SpeechOperations.Syncrecognize
对象的execute
() 方法来执行HTTP请求。
execute
()方法返回值是一个SyncRecognizeResponse
对象,它包含几个可选记录。这里,我们使用选择第一项。
// Create request SyncRecognizeRequest request = new SyncRecognizeRequest(); request.setConfig(recognitionConfig); request.setAudio(recognitionAudio); // Generate response SyncRecognizeResponse response = speechService.speech() .syncrecognize(request) .execute(); // Extract transcript SpeechRecognitionResult result = response.getResults().get(0); final String transcript = result.getAlternatives().get(0) .getTranscript();
最后,将文本传递给TextView
控件来显示。当然,只能在UI线程更新UI,请确保调用activity的runOnUiThread
()方法之后执行此操作。
runOnUiThread(new Runnable() { @Override public void run() { TextView speechToTextResult = (TextView)findViewById(R.id.speech_to_text_result); speechToTextResult.setText(transcript); } });
运行应用程序,选择包含美式英语演讲的FLAC文件,并查看Cloud Speech API为其生成文本。
要注意,Cloud Speech API目前只能处理单声道音频文件。如果发送多个频道的文件会报错。
5.使用Cloud Natural Language API
现在有一个文本,将其传递给Cloud Natural Language API进行分析。为了简单起见,我们只进行实体和情绪分析。 换句话说,我们要确定文本中提到的所有实体,如人员,地点和职业,还要判断其总体情绪是消极的,中立的还是积极的。
步骤1:更新布局
为了让用户开始,布局必须包含另一个Button
控件。将以下代码添加到activity的XML布局文件中:
<Button android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/browse_button" android:id="@+id/analyze_button" android:text="Analyze"/>
步骤2:Annotate the Transcript
Cloud Natural Language REST API提供了一个名为annotateText的选项 ,它让你只需一个HTTP请求即可在文档上运行情绪和实体分析。我们用它来分析文本。
因为分析必须在用户点击按钮时开始,需要添加一个OnClickListener
事件。
Button analyzeButton = (Button)findViewById(R.id.analyze_button); analyzeButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { // More code here } });
要使用Google API Client库与API进行交互,必须使用CloudNaturalLanguage.Builder
类创建CloudNaturalLanguage
对象 。它的构造方法还需要HTTP传输和JSON工厂这两个参数。
此外,通过分配CloudNaturalLanguageRequestInitializer
实例给它,可以强制它将API密钥添加到所有请求中。
final CloudNaturalLanguage naturalLanguageService = new CloudNaturalLanguage.Builder( AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null ).setCloudNaturalLanguageRequestInitializer( new CloudNaturalLanguageRequestInitializer(CLOUD_API_KEY) ).build();
要使用API分析的所有文本必须放在Document
对象内。 该Document
对象还必须包含配置信息,如文本的语言,以及它是否被格式化为纯文本或HTML。代码如下:
String transcript = ((TextView)findViewById(R.id.speech_to_text_result)) .getText().toString(); Document document = new Document(); document.setType("PLAIN_TEXT"); document.setLanguage("en-US"); document.setContent(transcript);
接下来,必须创建一个Features
对象来指定你感兴趣的特征。以下代码显示了如何创建一个Features
对象,该对象表示你要提取实体并仅运行情绪分析。
Features features = new Features(); features.setExtractEntities(true); features.setExtractDocumentSentiment(true);
现在可以使用Document
和Features
对象来组合一个 AnnotateTextRequest
对象,该对象被传递给annotateText
()方法,然后生成AnnotateTextResponse
对象。
final AnnotateTextRequest request = new AnnotateTextRequest(); request.setDocument(document); request.setFeatures(features); AsyncTask.execute(new Runnable() { @Override public void run() { AnnotateTextResponse response = naturalLanguageService.documents() .annotateText(request).execute(); // More code here } }
请注意,我们正在开启一个新线程,因为在UI线程上不允许网络操作。
通过调用AnnotateTextResponse
对象的getEntities
()方法, 你可以从AnnotateTextResponse对象中提取实体列表。同样,你可以通过调用getDocumentSentiment
()方法来提取文本的整体情绪。 然而,为了获得情感的实际分数,你必须调用getScore
()返回float
值。
机智如你,情绪分数等于零意味着情绪是中性的,得分大于零意味着情绪是积极的,小于零的分数意味着情绪是负面的。
如何处理实体列表和情绪评当然取决于你。这里,我们使用一个AlertDialog
来显示它们。
final List<Entity> entityList = response.getEntities(); final float sentiment = response.getDocumentSentiment().getScore(); runOnUiThread(new Runnable() { @Override public void run() { String entities = ""; for(Entity entity:entityList) { entities += "\n" + entity.getName().toUpperCase(); } AlertDialog dialog = new AlertDialog.Builder(MainActivity.this) .setTitle("Sentiment: " + sentiment) .setMessage("This audio file talks about :" + entities) .setNeutralButton("Okay", null) .create(); dialog.show(); } });
使用上述代码,情绪分数将显示在对话框的标题中,实体列表将显示在其正文中。
如果运行APP,应该能够分析音频文件的内容以及转录它们。
结语
现在,你应该知道如何使用云语音和云自然语言API来创建一个Android APP了吧,它不仅可以转录音频文件,还可以对其执行实体和情绪分析。在本教程中,你还学到了如何使用Android的存储访问框架和Google Client API库。
Google一直在给这两个API增加新的有趣功能,比如同时支持更多的语言。要保持最新状态,请参阅官方文档。
当你看到这里,你还可以看看关于移动APP云服务和机器学习的其他文章。
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post