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 的一段旅程。
參考: