Azure AI 服务之文本翻译
当下人工智能可谓是风头正劲,几乎所有的大厂都有相关的技术栈。微软在 AI 领域自然也是投入了重注,并且以 Azure 认知服务的方式投入了市场:
也就是说作为开发者我们不需要学习太多 AI 的理论知识,直接使用 Azure 提供的认知服务 API 就可以在程序中实现 AI 的功能了!
本文作为介绍 Azure AI 服务系列的第一篇,将通过 demo 介绍 Azure 认识服务中 Language 分类中的文本翻译服务(Translator Text API )。
Microsoft 文本翻译 API 是一种基于云的机器翻译服务, 支持多种语言。使用者可用于构建应用程序、网站、工具或任何需要多语言支持的解决方案。该服务是通过 REST API 提供的,所以我们可以以任何语言来调用它们。本文笔者使用 C# 通过构建一个 WPF 程序来演示如何通过简单的几步就能创建一个像模像样的翻译程序:
本文的完整 demo 请从这里下载。
创建 Azure 服务
要使用 Azure 的翻译服务需要先在 Azure 上创建对应的实例,比如我们需要先创建一个 "Translator Text API" 服务实例:
在本文的 demo 程序中我们还会用到拼写检查的服务,所以还需要创建一个 "Bing Spell Check v7 API" 服务的实例:
说明:对于学习和练习来说,你可以创建免费的 Azure 账号并创建免费版的上述实例,详细信息请参考 Azure 官网。
创建 WPF 应用程序
先在 VS 中创建 WPF 程序并简单的布局。
既然是 REST API,那么我们肯定是以 url 的方式访问服务,下面分别是访问文本翻译服务和拼写检查服务的 url:
const string TEXT_TRANSLATION_API_ENDPOINT = "https://api.microsofttranslator.com/v2/Http.svc/"; const string BING_SPELL_CHECK_API_ENDPOINT = "https://api.cognitive.microsoft.com/bing/v7.0/spellcheck/";
在访问相应的服务时,我们用这两个常量再拼接上合适的参数就可以了。
需要注意的是,Azure 提供的认知服务 API 都是需要认证信息的。具体的方式就是把我们创建的服务的 key 随 API 发送的服务器端进行认证,比如把 key 添加到 http 请求的 header 中:
WebRequest.Headers.Add("Ocp-Apim-Subscription-Key", "your key");
你可以在创建的服务实例的详情界面获得对应的 key,我们在程序中通过定义的常量来保存它们:
const string TEXT_TRANSLATION_API_SUBSCRIPTION_KEY = "your translator key"; const string BING_SPELL_CHECK_API_SUBSCRIPTION_KEY = "your spell check key";
由于 demo 的代码比较长,为了能集中精力介绍 Azure AI 相关的内容,本文中只贴出相关的代码。完整的 demo 代码在这里。
获取支持的语言列表
在进行任何的文本翻译之前,我们需要搞清楚 Azure 提供的翻译服务究竟支持哪些语言!下面的请求能够返回翻译服务支持的语言列表:
string uri = TEXT_TRANSLATION_API_ENDPOINT + "GetLanguagesForTranslate?scope=text";
我们把代码封装到下面的函数中:
private string[] languageCodes; private void GetLanguagesForTranslate() { // 获得翻译服务支持的语言 string uri = TEXT_TRANSLATION_API_ENDPOINT + "GetLanguagesForTranslate?scope=text"; WebRequest WebRequest = WebRequest.Create(uri); // 在 http 请求中添加认证信息 WebRequest.Headers.Add("Ocp-Apim-Subscription-Key", TEXT_TRANSLATION_API_SUBSCRIPTION_KEY); WebResponse response = null; // 把返回的 xml 信息抽取到数组中 response = WebRequest.GetResponse(); using (Stream stream = response.GetResponseStream()) { DataContractSerializer dcs = new DataContractSerializer(typeof(List<string>)); List<string> languagesForTranslate = (List<string>)dcs.ReadObject(stream); languageCodes = languagesForTranslate.ToArray(); } }
执行这个函数后,languageCodes 中的内容如下图所示:
虽然取到了可以翻译的语言列表,但是像图中的内容是无法显示给用户的,还需要把它们转换成对用户友好的名称,因此我们定义 GetLanguageNames 函数完成这个功能:
private SortedDictionary<string, string> languageCodesAndTitles = new SortedDictionary<string, string>(Comparer<string>.Create((a, b) => string.Compare(a, b, true))); private void GetLanguageNames() { // 获得简体中文的语言名称 string uri = TEXT_TRANSLATION_API_ENDPOINT + "GetLanguageNames?locale=zh-CHS"; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); request.Headers.Add("Ocp-Apim-Subscription-Key", TEXT_TRANSLATION_API_SUBSCRIPTION_KEY); request.ContentType = "text/xml"; request.Method = "POST"; DataContractSerializer dcs = new DataContractSerializer(Type.GetType("System.String[]")); using (Stream stream = request.GetRequestStream()) { dcs.WriteObject(stream, languageCodes); } // 把返回的 xml 信息抽取到数组中 var response = request.GetResponse(); string[] languageNames; using (Stream stream = response.GetResponseStream()) { languageNames = (string[])dcs.ReadObject(stream); } // 把支持的语言列表及其友好名称保存到字典数据结构中, // 随后会把它们绑定给 combo box 控件进行显示 for (int i = 0; i < languageNames.Length; i++) { languageCodesAndTitles.Add(languageNames[i], languageCodes[i]); } }
这次我们拿到了用中文显示的语言名称:
初始化源和目标语言列表
当获得了支持翻译的语言列表后,就可以通过 UI 控件把它们显示出来:
private void PopulateLanguageMenus() { int count = languageCodesAndTitles.Count; foreach (string menuItem in languageCodesAndTitles.Keys) { FromLanguageComboBox.Items.Add(menuItem); ToLanguageComboBox.Items.Add(menuItem); } // 设置默认的源语言和目标语言 FromLanguageComboBox.SelectedItem = "英语"; ToLanguageComboBox.SelectedItem = "简体中文"; }
在我们的使用场景中,把默认的翻译文本设置为 "英语",翻译的目标语言为 "简体中文":
翻译文本
接下来介绍文本翻译的 API,其核心是下面的 url 请求:
TEXT_TRANSLATION_API_ENDPOINT + "Translate?text=" + 待翻译文本 + "&from=" + 源语言 + "&to=" + 目标语言
同样,我们把它封装成一个具有完整功能的函数:
private void TranslateButton_Click(object sender, EventArgs e) { string textToTranslate = TextToTranslate.Text.Trim(); string fromLanguage = FromLanguageComboBox.SelectedValue.ToString(); string fromLanguageCode = languageCodesAndTitles[fromLanguage]; string toLanguageCode = languageCodesAndTitles[ToLanguageComboBox.SelectedValue.ToString()]; // 如果要翻译的文本是英语,还可以进行拼写检查 if (fromLanguageCode == "en") { textToTranslate = CorrectSpelling(textToTranslate); // 把更新后的文本保存到 UI 控件上 TextToTranslate.Text = textToTranslate; } // 处理文本为空和不需要翻译的情况 if (textToTranslate == "" || fromLanguageCode == toLanguageCode) { TranslatedText.Text = textToTranslate; return; } // 通过 http 请求执行翻译任务 string uri = string.Format(TEXT_TRANSLATION_API_ENDPOINT + "Translate?text=" + System.Web.HttpUtility.UrlEncode(textToTranslate) + "&from={0}&to={1}", fromLanguageCode, toLanguageCode); var translationWebRequest = HttpWebRequest.Create(uri); translationWebRequest.Headers.Add("Ocp-Apim-Subscription-Key", TEXT_TRANSLATION_API_SUBSCRIPTION_KEY); WebResponse response = null; response = translationWebRequest.GetResponse(); // 把返回的翻译结果抽取到 UI 控件中 Stream stream = response.GetResponseStream(); StreamReader translatedStream = new StreamReader(stream, Encoding.GetEncoding("utf-8")); System.Xml.XmlDocument xmlResponse = new System.Xml.XmlDocument(); xmlResponse.LoadXml(translatedStream.ReadToEnd()); TranslatedText.Text = xmlResponse.InnerText; }
在调用翻译文本的 API 前,需要先从 UI 控件中取得用户设置的源语言和目标语言,并且还要对放在 url 中传输的文本内容进行编码:
string uri = string.Format(TEXT_TRANSLATION_API_ENDPOINT + "Translate?text=" + System.Web.HttpUtility.UrlEncode(textToTranslate) + "&from={0}&to={1}", fromLanguageCode, toLanguageCode);
拼写检查
对于英语,我们可以通过 Bing Spell Check 服务进行翻译前的拼写检查。比如 TranslateButton_Click 函数中的:
// 如果要翻译的文本是英语,还可以进行拼写检查 if (fromLanguageCode == "en") { textToTranslate = CorrectSpelling(textToTranslate); // 把更新后的文本保存到 UI 控件上 TextToTranslate.Text = textToTranslate; }
主要的拼写检查逻辑被封装在了 CorrectSpelling 函数中:
private string CorrectSpelling(string text) { string uri = BING_SPELL_CHECK_API_ENDPOINT + "?mode=spell&mkt=en-US"; // 创建拼写检查的请求 HttpWebRequest spellCheckWebRequest = (HttpWebRequest)WebRequest.Create(uri); spellCheckWebRequest.Headers.Add("Ocp-Apim-Subscription-Key", BING_SPELL_CHECK_API_SUBSCRIPTION_KEY); spellCheckWebRequest.Method = "POST"; spellCheckWebRequest.ContentType = "application/x-www-form-urlencoded"; // 这个设置是必须的! // 把文本内容放在请求的 body 中 string body = "text=" + System.Web.HttpUtility.UrlEncode(text); byte[] data = Encoding.UTF8.GetBytes(body); spellCheckWebRequest.ContentLength = data.Length; using (var requestStream = spellCheckWebRequest.GetRequestStream()) requestStream.Write(data, 0, data.Length); HttpWebResponse response = (HttpWebResponse)spellCheckWebRequest.GetResponse(); // 从返回中取出 json 格式的拼写检查结果 var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); var responseStream = response.GetResponseStream(); var jsonString = new StreamReader(responseStream, Encoding.GetEncoding("utf-8")).ReadToEnd(); dynamic jsonResponse = serializer.DeserializeObject(jsonString); var flaggedTokens = jsonResponse["flaggedTokens"]; // 我们定义一个规则来应用拼写检查的结果, // 比如:当拼写检查的权值大于 0.7 时就用建议的值替换掉文本中的值。 var corrections = new SortedDictionary<int, string[]>(Comparer<int>.Create((a, b) => b.CompareTo(a))); for (int i = 0; i < flaggedTokens.Length; i++) { var correction = flaggedTokens[i]; var suggestion = correction["suggestions"][0]; if (suggestion["score"] > (decimal)0.7) corrections[(int)correction["offset"]] = new string[] { correction["token"], suggestion["suggestion"] }; } foreach (int i in corrections.Keys) { var oldtext = corrections[i][0]; var newtext = corrections[i][1]; if (text.Substring(i, oldtext.Length).All(char.IsUpper)) newtext = newtext.ToUpper(); else if (char.IsUpper(text[i])) newtext = newtext[0].ToString().ToUpper() + newtext.Substring(1); text = text.Substring(0, i) + newtext + text.Substring(i + oldtext.Length); } return text; }
从上面的代码可以看出,拼写检查只是给出一些建议,具体怎么做还是由用户决定的。比如上面的代码中当拼写检查的权值大于 0.7 时就用建议的值替换掉文本中的值。下面我们来测试一下拼写检查的逻辑,运行程序,并输入 "helo world!" 进行翻译:
执行翻译操作,代码逻辑在检测到待翻译的语言为英语时,会先进行代码的拼写检查:
上图显示拼写检查函数 CorrectSpelling 纠正了我们的拼写错误,下面是翻译的结果:
有了代码的拼写检查,是不是感觉这个程序有点 "智能" 的味道啦!
总结
就像 azure 提供的其它服务一样,入门和上手非常的容易。我们简单的搞了几下就能够运行一个简单的文本翻译程序了。
当然这只是一个开始,希望大家和笔者一道通过本文开启 Azure AI 的一段旅程。