こんな記事を見つけたので、Azure Cognitive Service の Text Translation API でニューラルネットワークを使ったテキスト翻訳を試してみました。
https://blogs.msdn.microsoft.com/jpcie/?p=555
今回も Azure の Bot Services を使ったBot アプリを作り、Web Chat 化してみました。
成果物は以下のもので、英語の文章を与えると普通のテキスト翻訳、ニューラルネットワークを使ったテキスト翻訳の2つを返してくれます。
Google 翻訳がニューラルネットワークを採用したことで、テキスト翻訳の精度が高くなったことは、ご存知の方も多いと思います。
ということで、Goole 翻訳とも比較してみたいと思います。
今回は、英語の文章を日本語化するので、英語の文章を Get してくる必要があります。
ちょうどいい感じに、NASA の発表があったので、Web より文章を拝借しました。
https://www.nasa.gov/press-release/nasa-telescope-reveals-largest-batch-of-earth-size-habitable-zone-planets-around
「NASA’s Spitzer Space Telescope has revealed the first known system of seven Earth-size planets around a single star. Three of these planets are firmly located in the habitable zone, the area around the parent star where a rocky planet is most likely to have liquid water.」
Google 翻訳
Text Translation API
結果のまとめ
- Google
NASAのスピッツァー宇宙望遠鏡(Spitzer Space Telescope)は、単一の星の周りに7つの地球サイズの惑星の最初の既知のシステムを明らかにした。これらの惑星のうちの3つは、岩石の多い惑星が液体の水をもっている可能性が最も高い親スターの周りの居住可能ゾーンにしっかりと位置しています。 - Text Translation API (普通)
NASA のスピッツァー宇宙望遠鏡は、1 つの星の周り 7 地球サイズの惑星の最初の知られているシステムを明らかにしました。これらの惑星の 3 つはしっかりとハビタブル ゾーン、岩の多い惑星が液体の水が存在する最も可能性の高い親星周辺に位置しています。 - Text Translation API (ニューラルネットワーク)
nasa のスピッツァー宇宙望遠鏡は、単一の星の周りに7つの地球規模の惑星の最初の既知のシステムを明らかにした。これらの惑星の3つは、しっかりと居住ゾーンに位置しています, 岩の惑星が最も液体の水を持っている可能性が高い親星の周りの領域.
この文章だけなら、好みの問題なのかなー。。。
Text Translation API の普通のとニューラルネットワークのいいとこどりをすると Google 翻訳に近い感じの文章になりそうな気もするけど、どちらにしても意味は分かるけどわかりにく文章っすね。
もうちょっと一般的な文章を食わせてみると面白いかも
今回のソース
今回のソースです。
「Azure Bot Services で日本語を英語に翻訳する Bot を作ってみた」の時のソースをほぼ丸パクリです。
変えたのは、以下2点くらいです。
- Text Translation API を呼び出すURIの作成時にcategoryのオプションを追加した
- Text Translation API の Key の値をAzure App Services のApplication Settings に寄せた
#r "System.Web" using System; using System.Net; using System.Net.Http; using System.Threading.Tasks; using System.IO; using System.Configuration; using Microsoft.Bot.Builder.Dialogs; using Microsoft.Bot.Connector; // For more information about this template visit http://aka.ms/azurebots-csharp-basic [Serializable] public class EchoDialog : IDialog<object> { protected int count = 1; private string SubscriptionKey = ConfigurationManager.AppSettings["TextTranslationKey"]; public Task StartAsync(IDialogContext context) { try { context.Wait(MessageReceivedAsync); } catch (OperationCanceledException error) { return Task.FromCanceled(error.CancellationToken); } catch (Exception error) { return Task.FromException(error); } return Task.CompletedTask; } public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument) { var message = await argument; if (message.Text == "reset") { PromptDialog.Confirm( context, AfterResetAsync, "Are you sure you want to reset the count?", "Didn't get that!", promptStyle: PromptStyle.Auto); } else { var authTokenSource = new AzureTextTransfer(SubscriptionKey); var token = string.Empty; try { token = await authTokenSource.GetAccessTokenAsync(); } catch (HttpRequestException) { switch (authTokenSource.RequestStatusCode) { case HttpStatusCode.Unauthorized: Console.WriteLine("Request to token service is not authorized (401). Check that the Azure subscription key is valid."); break; case HttpStatusCode.Forbidden: Console.WriteLine("Request to token service is not authorized (403). For accounts in the free-tier, check that the account quota is not exceeded."); break; } throw; } //var msgEnglish = authTokenSource.TextTransfer(token, message.Text,"ja","en"); //var msgRejapanese = authTokenSource.TextTransfer(token, msgEnglish,"en","ja"); //await context.PostAsync(msgEnglish); //await context.PostAsync(msgRejapanese); var msgGeneral = authTokenSource.TextTransfer(token, message.Text,"en","ja"); var msgGeneralnn = authTokenSource.TextTransfer(token, message.Text,"en","ja","generalnn"); await context.PostAsync(msgGeneral); await context.PostAsync(msgGeneralnn); context.Wait(MessageReceivedAsync); } } public async Task AfterResetAsync(IDialogContext context, IAwaitable<bool> argument) { var confirm = await argument; if (confirm) { this.count = 1; await context.PostAsync("Reset count."); } else { await context.PostAsync("Did not reset count."); } context.Wait(MessageReceivedAsync); } } public class AzureTextTransfer { /// URL of the token service private static readonly Uri ServiceUrl = new Uri("https://api.cognitive.microsoft.com/sts/v1.0/issueToken"); /// Name of header used to pass the subscription key to the token service private const string OcpApimSubscriptionKeyHeader = "Ocp-Apim-Subscription-Key"; /// After obtaining a valid token, this class will cache it for this duration. /// Use a duration of 5 minutes, which is less than the actual token lifetime of 10 minutes. private static readonly TimeSpan TokenCacheDuration = new TimeSpan(0, 5, 0); /// Cache the value of the last valid token obtained from the token service. private string storedTokenValue = string.Empty; /// When the last valid token was obtained. private DateTime storedTokenTime = DateTime.MinValue; /// Gets the subscription key. public string SubscriptionKey { get; private set; } /// Gets the HTTP status code for the most recent request to the token service. public HttpStatusCode RequestStatusCode { get; private set; } /// /// <param name="key">Subscription key to use to get an authentication token.</param> public AzureTextTransfer(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentNullException("key", "A subscription key is required"); } this.SubscriptionKey = key; this.RequestStatusCode = HttpStatusCode.InternalServerError; } /// <returns>The encoded JWT token prefixed with the string "Bearer ".</returns> /// <remarks> /// This method uses a cache to limit the number of request to the token service. /// A fresh token can be re-used during its lifetime of 10 minutes. After a successful /// request to the token service, this method caches the access token. Subsequent /// invocations of the method return the cached token for the next 5 minutes. After /// 5 minutes, a new token is fetched from the token service and the cache is updated. /// </remarks> public async Task<string> GetAccessTokenAsync() { // Re-use the cached token if there is one. if ((DateTime.Now - storedTokenTime) < TokenCacheDuration) { return storedTokenValue; } using (var client = new HttpClient()) using (var request = new HttpRequestMessage()) { request.Method = HttpMethod.Post; request.RequestUri = ServiceUrl; request.Content = new StringContent(string.Empty); request.Headers.TryAddWithoutValidation(OcpApimSubscriptionKeyHeader, this.SubscriptionKey); var response = await client.SendAsync(request); this.RequestStatusCode = response.StatusCode; response.EnsureSuccessStatusCode(); var token = await response.Content.ReadAsStringAsync(); storedTokenTime = DateTime.Now; storedTokenValue = "Bearer " + token; return storedTokenValue; } } public String TextTransfer(string AppID,string textToTransfer, string from, string to,string category="general") { string translation = String.Empty; string uri = "https://api.microsofttranslator.com/v2/http.svc/Translate?appid="+ AppID + "&text=" + System.Web.HttpUtility.UrlEncode(textToTransfer) + "&from=" + from + "&to=" + to + "&category=" + category; HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(uri); WebResponse response = null; try { response = httpWebRequest.GetResponse(); using (Stream stream = response.GetResponseStream()) { System.Runtime.Serialization.DataContractSerializer dcs = new System.Runtime.Serialization.DataContractSerializer(Type.GetType("System.String")); translation = (string)dcs.ReadObject(stream); } } catch { throw; } finally { if (response != null) { response.Close(); response = null; } } return (translation); } }
まとめ
Text Transer API は割と簡単に使えるし、英語しか対応してないAPIを使うときとかにも有効だと思うので、結構使い道があるんじゃないかなって思います。
翻訳を試してみたい方は、以下のサイトで簡単に試すこともできます。
https://translator.microsoft.com/neural
機械翻訳が進化したら英語の勉強は不要になるか的な記事をたまに見ますが、どうなんでしょうねー。
コメント