まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:customize

先日、IBM Bluemix から提供されている IBM Watson の翻訳 API である "Language Translator" サービスが待望の日本語対応を果たしました。現在、日本語翻訳機能としては英語→日本語、および日本語→英語のテキスト翻訳が可能になっています:
2016122808


この翻訳 API は文章の翻訳に加え、入力されたテキストが何語のテキストなのかを識別する機能も備えています。実際にどんな言語翻訳ができるのか、そのサンプル的なアプリケーションが以下で公開されているので、興味ある方は試してみてください:
https://language-translator-demo.mybluemix.net/

2016122809



さて、この IBM Watson 翻訳 API の特徴の1つが「カスタマイズ機能」です。デフォルトで提供される翻訳機能をベースに、自分なりのカスタマイズを加えることができるようになっています。これは日本語翻訳機能においても有効な機能です。

というわけで、早速カスタマイズを試してみましょう。ただし、このカスタマイズを使うには、Language Translator サービスは標準プランではなく、拡張プランを選択する必要があります。標準プランには無料枠が用意されていますが、拡張プランにはありません。サインアップから 30 日間の無料期間を過ぎていたり、既に有償プランに移行済みの場合は、サービスインスタンスを作成した段階でインスタンス料金が発生しますので、ご注意ください:
2016122801


では「拡張プラン」を選択していることを確認した上でインスタンスを「作成」します:
2016122802


こうして拡張プランの Language Translator サービスインスタンスを作りました。この後、実際に API を使うことになるので「サービス資格情報」の「資格情報の表示」からクレデンシャル情報を調べてメモしておいてください(繰り返しですが、拡張プランの資格情報です。流出すると勝手に使われて課金額がどんどん増えていくことになるので、取扱いに注意してください):
2016122803


次に実際にカスタマイズする内容を TMX(Transaction Memory eXchange) という翻訳メモリデータの標準フォーマットである XML ファイルで用意します。今回は以下の内容の osaka.tmx ファイルを手元で作って用意しました:
<tmx version="1.4">
  <header
    creationtool="MyTool" creationtoolversion="1.00"
    datatype="PlainText" segtype="sentence"
    adminlang="en-ja" srclang="en"
    o-tmf="MyTransMem"/>
  <body>
    <tu>
      <tuv xml:lang="en">
        <seg>Hello</seg>
      </tuv>
      <tuv xml:lang="ja">
        <seg>もうかりまっか</seg>
      </tuv>
    </tu>
    <tu>
      <tuv xml:lang="en">
        <seg>I am fine</seg>
      </tuv>
      <tuv xml:lang="ja">
        <seg>ぼちぼちでんな</seg>
      </tuv>
    </tu>
  </body>
</tmx>

(かなり偏見入ってますが・・)大阪弁のカスタマイズをするためのファイルです。英語の "Hello" を「もうかりまっか」に、"I am fine" を「ぼちぼちでんな」に変換する、という内容です。これを元々の英→日翻訳機能に上書きする形でカスタマイズする、という意味です。なお日→英の翻訳カスタマイズは今回の説明には含めませんが、同様にして(同様の TMX ファイルを別途用意する形で)カスタマイズすることは可能です。


これでカスタマイズの準備はできました。 が、カスタマイズの前に、カスタマイズ前の挙動を確認しておきます(後でカスタマイズ後の挙動と比較するためです)。ここからは curl コマンドを使って実際に API を使ってみるので、必要に応じてここなどから curl コマンドをインストールしておいてください。

まず最初に、Language Translator がデフォルトで用意しているモデル(日英とか、英日とか、英仏とか、・・)の一覧を取得してみます(username と password には上記の資格情報で取得したものを指定します。また黒字が入力コマンド、青字が出力結果です):
$ curl -u "username:password" "https://gateway.watsonplatform.net/language-translator/api/v2/models"

{
  "models":[
    {
      "model_id":"ar-en",
      "source":"ar",
      "target":"en",
      "base_model_id":"",
      "domain":"news",
      "customizable":true,
      "default_model":true,
      "owner":"",
      "status":"available",
      "name":"",
      "train_log":null
    },
      :
    {
      "model_id":"en-ja",
      "source":"en",
      "target":"ja",
      "base_model_id":"",
      "domain":"news",
      "customizable":true,
      "default_model":true,
      "owner":"",
      "status":"available",
      "name":"",
      "train_log":null
    },
      :
    {
      "model_id":"ja-en",
      "source":"ja",
      "target":"en",
      "base_model_id":"",
      "domain":"news",
      "customizable":true,
      "default_model":true,
      "owner":"",
      "status":"available",
      "name":"",
      "train_log":null
    },
      :
  ]
}

モデルの一覧が JSON 配列形式で出力されます。"model_id" の値が実際に翻訳する時やこの後のカスタマイズ時に翻訳の種類を指定する値になります。英日であれば "en-ja" 、日英であれば "ja-en" という値になります。

また各モデルの中に "customizable" という値があります。これは「このモデルをカスタマイズ可能か」を示した値であり、true になっているとカスタマイズ可能であることを示しており、このモデルに上書きする形でのカスタマイズが可能です(英日、日英ともに true です)。また "status" の値は現時点で利用可能な状態になっているかどうかを示しています(これも英日、日英ともに "available" となっているので使える状態になっています)。

ではカスタマイズ前の翻訳機能を試してみましょう。英語の "Hello" を日本語に翻訳してみます:
$ curl -u "username:password" "https://gateway.watsonplatform.net/language-translator/api/v2/translate?model_id=en-ja&text=Hello"

こんにちは

カスタマイズ前(model_id に既存の en-ja を指定)なので、期待通りの結果になりました。

ではここからはカスタマイズを加えてみましょう。上記で用意した osaka.tmx を指定して、以下のコマンドを実行します:
$ curl -u "username:password" -X POST -F base_model_id="en-ja" -F forced_glossary=@osaka.tmx "https://gateway.watsonplatform.net/language-translator/api/v2/models"

{
  "model_id":"be81b082-1292-47ac-badf-0beb83846f66",
  "source":"en",
  "target":"ja",
  "base_model_id":"en-ja",
  "domain":"news",
  "customizable":false,
  "default_model":false,
  "owner":"da0dc7f3-87c6-4d82-b965-79b463266291",
  "status":"dispatching",
  "name":null,
  "train_log":null
}

基本モデル(base_model_id)を英日(en-ja)にして、そこに osaka.tmx で指定した内容を上書きでカスタマイズする、という内容のコマンドです。実行結果には新しい model_id (be81b082-1292-47ac-badf-0beb83846f66)が付与されて返っています。また customizable 値は false になっているので、このモデルをベースに更にカスタマイズ、ということはできません。status は "dispatching" となっているので、この時点ではまだ内部処理中です。

少し待ってから再度ステータスを確認し、"available" となっていることを確認できたら翻訳も可能になります。
$ curl -u "username:password" "https://gateway.watsonplatform.net/language-translator/api/v2/models/be81b082-1292-47ac-badf-0beb83846f66"

{
  "model_id":"be81b082-1292-47ac-badf-0beb83846f66",
  "source":"en",
  "target":"ja",
  "base_model_id":"en-ja",
  "domain":"news",
  "customizable":false,
  "default_model":false,
  "owner":"da0dc7f3-87c6-4d82-b965-79b463266291",
  "status":"available",
  "name":null,
  "train_log":null
}

また、この段階でモデル一覧を再確認すると、カスタマイズしたこのモデルも一覧の先頭に含まれて表示されることがわかります:
$ curl -u "username:password" "https://gateway.watsonplatform.net/language-translator/api/v2/models"

{
  "models":[
    {
      "model_id":"be81b082-1292-47ac-badf-0beb83846f66",
      "source":"en",
      "target":"ja",
      "base_model_id":"en-ja",
      "domain":"news",
      "customizable":false,
      "default_model":false,
      "owner":"da0dc7f3-87c6-4d82-b965-79b463266291",
      "status":"available",
      "name":null,
      "train_log":null
    },
    {
      "model_id":"ar-en",
      "source":"ar",
      "target":"en",
      "base_model_id":"",
      "domain":"news",
      "customizable":true,
      "default_model":true,
      "owner":"",
      "status":"available",
      "name":"",
      "train_log":null
    },
      :
    {
      "model_id":"en-ja",
      "source":"en",
      "target":"ja",
      "base_model_id":"",
      "domain":"news",
      "customizable":true,
      "default_model":true,
      "owner":"",
      "status":"available",
      "name":"",
      "train_log":null
    },
      :
    {
      "model_id":"ja-en",
      "source":"ja",
      "target":"en",
      "base_model_id":"",
      "domain":"news",
      "customizable":true,
      "default_model":true,
      "owner":"",
      "status":"available",
      "name":"",
      "train_log":null
    },
      :
  ]
}

ではこのカスタマイズモデルを model_id に指定して、先程と同じ英語を翻訳してみます:
$ curl -u "username:password" "https://gateway.watsonplatform.net/language-translator/api/v2/translate?model_id=be81b082-1292-47ac-badf-0beb83846f66&text=Hello"

もうかりまっか

期待通りのカスタマイズが有効な変換結果になりました。なお "I am fine" でもカスタマイズした結果が翻訳されますが、それ以外の(カスタマイズしていない)テキストについてはベースとなっている英日翻訳が有効になって実行されます:
$ curl -u "username:password" "https://gateway.watsonplatform.net/language-translator/api/v2/translate?model_id=be81b082-1292-47ac-badf-0beb83846f66&text=I%20am%20fine"

ぼちぼちでんな

$ curl -u "username:password" "https://gateway.watsonplatform.net/language-translator/api/v2/translate?model_id=be81b082-1292-47ac-badf-0beb83846f66&text=Good%20evening"

こんばんは

カスタマイズモデルが不要になった場合は以下のコマンドで削除することができます:
$ curl -u "username:password" -X DELETE "https://gateway.watsonplatform.net/language-translator/api/v2/models/be81b082-1292-47ac-badf-0beb83846f66"

{"status":"OK"}

今回紹介したのはごくシンプルなカスタマイズでしたが、より多くの語彙を用意できれば方言や、或いは全く新しい言語の翻訳機能まで実現することができるかもしれません。そんなカスタマイズ機能が IBM Watson の Language Translator API には提供されている、という紹介でした。


なお、Language Translator API の詳しい関数リファレンスはこちらを参照ください:
https://www.ibm.com/watson/developercloud/language-translator/api/v2/

実際に挙動をためせる Watson API Explorer はこちら:
https://watson-api-explorer.mybluemix.net/apis/language-translator-v2#/


#TMX をもう少し楽に作れるツールとかないかな・・・


IBM Notes の起動時に表示されるスプラッシュスクリーンをカスタマイズする方法を紹介します:
2015091801
 (↑これがスプラッシュスクリーン)


やり方は色々あって、「きちんと」対応しようとすると結構面倒なこともあるんですが、てっとり早く作るにはこれかなあ、という方法を紹介します。

まずはビットマップ(Windows BMP)形式のスクリーン画像を用意します。今回はこれを使います:
2015091802
(注 この画像↑自体は PNG 形式なので、保存してもそのままでは使えません。お手元の環境で BMP 形式に変換してから使ってください)

なお、ここで用意する画像のサイズがあまり小さすぎると、パスワードプロンプトメッセージがうまく表示できなかったりするので、適宜調整してください。ちなみに↑上記の画像のサイズは 640x480 です。

この BMP 画像ファイルを適当なフォルダに保存します(C:\IBM\Notes\shojoji.bmp に保存したとします)。そしていったんノーツを閉じて、notes.ini ファイルをテキストエディタで開き、最後に以下の2行を追加します:
  :
  :
SESPlashPath=C:\IBM\Notes\shojoji.bmp
HasNotesOverlay=1

この状態で notes.ini を保存して、改めてノーツを起動するとこんな感じになります。指定した画像がスプラッシュスクリーンになって、パスワード入力待ちになっていることが確認できます:
2015091803


スプラッシュスクリーンをよく見ると分かるのですが、画像左下にパスワードプロンプトメッセージが表示されていたりします。画像サイズによってはここが崩れてしまったり、(今回のように)見難い位置に表示されてしまったりする可能性があるので、そのあたり上手く調整する必要があります。


業務でノーツをお使いの皆様、仕事中の気分転換にどうぞ。

なお、「ちゃんとした方法」はこちらをどうぞ:
http://www-01.ibm.com/support/docview.wss?uid=swg21962056


Tomcat や Jetty などの Java ウェブアプリケーションでエラーが発生した時に表示されるエラーページを独自のものにカスタマイズする方法の紹介です。もちろんエラーが発生しないことが望ましいのですが、万が一エラーが発生してしまった場合に、標準のエラーメッセージが出てしまうと、どのアプリケーションサーバーを使っているのかが分かってしまいます。その結果、そのサーバーのセキュリティホールを狙われる可能性もないわけではありません。あとエラーページにユーモアを交えるような目的でも有用だと思います。

ではその手順の紹介です。 まずアプリケーションの web.xml の最後に以下の青字の情報を追加します:
  :
  <welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
  </welcome-file-list>

  <error-page>
    <error-code>404</error-code>
    <location>/error/404.html</location>
  </error-page>
  <error-page>
    <error-code>500</error-code>
    <location>/error/500.jsp</location>
  </error-page>
</web-app>

この例ではステータスコードが 404(ページが見つからないエラー)の時と、500(サーバー内部エラー)の時用のページをカスタマイズする前提で、その2つのページに関する情報を追加しています。他のエラー(認証など)についてもカスタマイズする場合は同様に追加してください。この例ではページが見つからない場合は /error/404.html に、Java コード等での内部エラー発生時には /error/500.jsp にそれぞれ飛ばすような指定をしています。もちろん静的な HTML ページでもいいのですが、今回は 500 エラーの時には JSP にして動的に作成してみます。

そしてこれらのページを作っていきます。/error/404.html は 404 エラー、つまり URL が間違っていることになるので、こんな感じの内容で:
<html>
<head>
<title>404</title>
</head>
<body>
<h1>見つからないよ~</h1>
</body>
</html>

一方、/error/500.jsp は 500 エラー、つまり Java の内部エラーなので、スタックトレースも表示するような内容にしてみます。試しにこんな感じにしました。また isErrorPage を true にしています:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<%@ page import="javax.servlet.http.*" %>
<%@ page import="javax.servlet.*" %>
<% request.setCharacterEncoding("utf-8"); %>
<%@ page isErrorPage="true"%>

<html>
<head>
<title>500</title>
</head>
<body>
<h1>Internal な Server の Error だよ~</h1>

<% exception.printStackTrace(new java.io.PrintWriter(out)); %>


</body>
</html>

最後に、わざと 500 エラーを発生させるための JSP ページを作成します:
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" %>
<%@ page import="javax.servlet.http.*" %>
<%@ page import="javax.servlet.*" %>
<% request.setCharacterEncoding("utf-8"); %>

<%
String[] a = null;
for( int i = 0; i < a.length; i ++ ){ //. ここでぬるぽエラー
}
%>

<html>
<head>
<title>index</title>
</head>
<body>
<h1>Index</h1>

<% exception.printStackTrace(new java.io.PrintWriter(out)); %>


</body>
</html>

この状態で動かしてみます。まず存在しないページの URL を叩くとこんな感じのエラーになります:
2015012706


次に上記で作成したわざと NullPointer エラーがでるページにアクセスするとこんな感じに:
2015012707


できた!
 

このページのトップヘ