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

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

2015年01月

ウェブサーバー(HTTP サーバー)を使う場合、なんらかのアクセスログを残すことになります。例えば Apache HTTPD の場合、デフォルトでアクセスログは /var/log/httpd/access_log に残ります。そのフォーマットは /etc/httpd/conf/httpd.conf 内で設定/カスタマイズできます。
2015012200


デフォルトの httpd.conf では、以下の様なログフォーマットが指定されています:
LogFormat "%h %l %u %t \"%r\" %>s %b common

暗号のような記述ですが、左から順にこのような意味があります:
 %h : 接続元のリモートホスト名(IPアドレス)
 %l : クライアント識別子(取得できない場合は - )
 %u : 認証時のユーザー名(取得できない場合は - )
 %t : 時刻
 %r : リクエストの最初の行の値
 %s : レスポンスステータス
 %b : 送信バイト数(0バイトの場合は - )

このログフォーマットであれば、以下の様なログが取得できることになります:
192.168.1.101 - - [20/Jan/2015:11:32:29 +0900] "GET / HTTP/1.1" 200 -


最初の値がアクセス元のホストになります。つまりこれで(プロクシが使われている場合はプロクシのアドレスになりますが)どこからのアクセスがあったのか、という情報が分かります。


ところが、このアクセス元ホストが分からなくなる場合があります。それは HTTP サーバーがロードバランサ経由で使われていた場合です。サーバーの負荷軽減などの目的で、サーバーを複数台構成にしてロードバランサ経由で利用する、というケースは珍しくないと思います(クラウド環境によっては1台構成でもロードバランサ経由になることもあります)。ただその場合、この設定だとアクセス元ホストは必ずロードバランサの IP アドレスになってしまい、結果として常に同じホストからのアクセスがあったとログに記録されることになります。

これを避けるには httpd.conf をカスタマイズする必要があります。具体的にはこんな感じに:
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b common


%h の代わりに %{X-Forwarded-For}i という記述に変えました。この意味ですが、まず %{XXX}i という記述は「リクエストヘッダに含まれるヘッダー名 XXX の値」です。つまりこの記述は「リクエストヘッダに含まれる X-Forwarded-For ヘッダの値」をログに書き出すよう指示していることになります。

そしてリクエストヘッダの X-Forwarded-For ですが、これはプロクシやロードバランサなどのキャッシングサーバーに接続するクライアントの送信元 IP アドレスです。つまりキャッシングサーバーから見た時の %h の値がこのヘッダ値に入ってくるので、その値を取り出してログに記録すればよい、ということになるわけです。 そこで上記のような設定をすることでロードバランサ経由であっても、元のクライアントの IP アドレスを記録できるようになるのでした。

なお、この方法は HTTP プロトコルに限って有効です。HTTPS では暗号化によって HTTP ヘッダに送信元 IP アドレスが記録できるかどうか、ロードバランサによって変わってきてしまいます。お使いのロードバランサが HTTPS をサポートしているか、サポートしていない場合の対応方法などはお使いのクラウド業者に問い合わせる必要があると思っています。



 

IBM Bluemix のデータセンターとして、これまでの米国に加えてヨーロッパリージョン(英国)が追加されました:
Regions in Bluemix


IBM Bluemix 上ではヨーロッパリージョンを使っていると画面上部に「地域:英国」と表示されます:
2015012001
 
注意点として、ヨーロッパリージョンを使っている場合の cf コマンドの使い方が変わります。具体的にはログインコマンド実行時の指定 API エンドポイントがこれまでの
# cf login -a https://api.ng.bluemix.net -u (IBM ID) -p (パスワード)

ではなく、
# cf login -a https://api.eu-gb.bluemix.net -u (IBM ID) -p (パスワード)

のように指定する必要があります。 そして続けて組織とスペースを
# cf target -o (IBM ID) -s dev

のように指定します。

実は login コマンド自体は前者の指定のままでも成功するのですが、後者のように指定しないと次の target コマンド時や実際のプッシュ時などでエラーとなってしまいます。


以前に書いたこの↓ブログエントリも直しておかないと・・・
Bluemix の cf コマンド
 
 

このエントリの続きです。取得した XML の内容を詳しくみてみます。


改めて、まず API に入力したテキストは以下の様なものでした:
John Smith lives in New York, and he has been living there since 2001.


そして、API で取得した解析結果の XML はこのようなものでした:
<?xml version='1.0' encoding='utf-8'?>
<rep sts="OK">
<doc id="">
  <text>John Smith lives in New York, and he has been living there since 2001.
</text>
  <sents>
    <sent sid="0" begin="0" end="15">
      <text>John Smith lives in New York, and he has been living there since 2001.</text>
      <parse score="-2.73025">[S [S [NP John_NNP Smith_NNP NP] [VP lives_VBZ [PP in_IN [NP New_NNP York_NNP NP] PP] VP] S] ,_, and_CC [S [NP he_PRP NP] [VP has_VBZ [VP been_VBN [VP living_VBG [ADVP there_RB ADVP] [PP since_IN [NP 2001_CD NP] PP] VP] VP] VP] S] ._. S]</parse>
      <dependency_parse>John NNP 1 -I Smith NNP 2 NP lives VBZ -1 VP in IN 2 PP New NNP 5 -I York NNP 3 NP , , 2 -E and CC 2 -E he PRP 9 NP has VBZ 2 VP been VBN 9 VP living VBG 10 VP there RB 11 ADVP since IN 11 PP 2001 CD 13 NP . . 2 -E </dependency_parse>
      <usd_dependency_parse>John NNP 1 dep Smith NNP 2 npmod lives VBZ -1 root in IN 5 case New NNP 5 dep York NNP 2 nmod , , 2 dep and CC 2 dep he PRP 9 npmod has VBZ 2 VP been VBN 9 VP living VBG 10 VP there RB 11 ADVP since IN 14 case 2001 CD 11 nmod . . 2 dep</usd_dependency_parse>
      <tokens>
        <token tid="0" begin="0" end="3" type="0">John</token>
        <token tid="1" begin="5" end="9" type="0">Smith</token>
        <token tid="2" begin="11" end="15" type="0">lives</token>
        <token tid="3" begin="17" end="18" type="0">in</token>
        <token tid="4" begin="20" end="22" type="0">New</token>
        <token tid="5" begin="24" end="27" type="0">York</token>
        <token tid="6" begin="28" end="28" type="2048">,</token>
        <token tid="7" begin="30" end="32" type="0">and</token>
        <token tid="8" begin="34" end="35" type="0">he</token>
        <token tid="9" begin="37" end="39" type="0">has</token>
        <token tid="10" begin="41" end="44" type="0">been</token>
        <token tid="11" begin="46" end="51" type="0">living</token>
        <token tid="12" begin="53" end="57" type="0">there</token>
        <token tid="13" begin="59" end="63" type="0">since</token>
        <token tid="14" begin="65" end="68" type="0">2001</token>
        <token tid="15" begin="69" end="69" type="6144">.</token>
      </tokens>
    </sent>
  </sents>
  <mentions>
    <mention mid="-M0" mtype="NAM" begin="0" end="9" head-begin="0" head-end="9" eid="-E0" etype="PERSON" role="PERSON" metonymy="0" class="SPC" score="0.995606" corefScore="1">John Smith</mention>
    <mention mid="-M1" mtype="NAM" begin="20" end="27" head-begin="20" head-end="27" eid="-E1" etype="GPE" role="LOCATION" metonymy="0" class="SPC" score="0.993534" corefScore="1">New York</mention>
    <mention mid="-M2" mtype="PRO" begin="34" end="35" head-begin="34" head-end="35" eid="-E0" etype="PERSON" role="PERSON" metonymy="0" class="SPC" score="0.955652" corefScore="0.417012">he</mention>
    <mention mid="-M3" mtype="NONE" begin="65" end="68" head-begin="65" head-end="68" eid="-E2" etype="DATE" role="DATE" metonymy="0" class="SPC" score="0.820601" corefScore="1">2001</mention>
  </mentions>
  <entities>
    <entity eid="-E0" type="PERSON" generic="0" class="SPC" level="NAM" subtype="OTHER" score="0.645765">
      <mentref mid="-M0">John Smith</mentref>
      <mentref mid="-M2">he</mentref>
    </entity>
    <entity eid="-E1" type="GPE" generic="0" class="SPC" level="NAM" subtype="OTHER" score="1">
      <mentref mid="-M1">New York</mentref>
    </entity>
    <entity eid="-E2" type="DATE" generic="0" class="SPC" level="NONE" subtype="OTHER" score="1">
      <mentref mid="-M3">2001</mentref>
    </entity>
  </entities>
  <relations version="KLUE2_cascaded:2011_10_25">
    <relation rid="-R1" type="residesIn" subtype="OTHER">
      <rel_entity_arg eid="-E0" argnum="1"/>
      <rel_entity_arg eid="-E1" argnum="2"/>
      <relmentions>
        <relmention rmid="-R1-1" score="0.525653" class="SPECIFIC" modality="ASSERTED" tense="UNSPECIFIED">
          <rel_mention_arg mid="-M0" argnum="1">John Smith</rel_mention_arg>
          <rel_mention_arg mid="-M1" argnum="2">New York</rel_mention_arg>
        </relmention>
      </relmentions>
    </relation>
  </relations>
  <sgml_sent_info/>
  <sgml_char_info/>
</doc>
</rep>

この XML を上から眺めていくと、入力データである "John Smith lives in New York, and he has been living there since 2001." が解析されて、トークンに分類され、それぞれどういった品詞なのかの分析がされ、・・・ という経緯が分かると思います。

その中に以下の <mentions> 要素が見て取れます:
  <mentions>
    <mention mid="-M0" mtype="NAM" begin="0" end="9" head-begin="0" head-end="9" eid="-E0" etype="PERSON" role="PERSON" metonymy="0" class="SPC" score="0.995606" corefScore="1">John Smith</mention>
    <mention mid="-M1" mtype="NAM" begin="20" end="27" head-begin="20" head-end="27" eid="-E1" etype="GPE" role="LOCATION" metonymy="0" class="SPC" score="0.993534" corefScore="1">New York</mention>
    <mention mid="-M2" mtype="PRO" begin="34" end="35" head-begin="34" head-end="35" eid="-E0" etype="PERSON" role="PERSON" metonymy="0" class="SPC" score="0.955652" corefScore="0.417012">he</mention>
    <mention mid="-M3" mtype="NONE" begin="65" end="68" head-begin="65" head-end="68" eid="-E2" etype="DATE" role="DATE" metonymy="0" class="SPC" score="0.820601" corefScore="1">2001</mention>
  </mentions>

Watson が解析した結果、0文字目から9文字目までの "John Smith" は 99.5606% の確率で一人の人間(PERSON)の名前(NAM)であり、同様に "New York" は 99.3534% の確率で1つの場所(LOCATION)の名前(NAM)だと解析しています。

また "he" は人間(PERSON)の代名詞(PRO)であり、かつ "John Smith" と同じ eid の値("-E0")になっていて、その関連確率は 41.7012% になっています。つまりこの "he" は約40%の確率で "John Smith" のことだ、と判断していることになります。この文脈だとその通りなので、ちゃんとその辺りを正しく判断できていることになります。

そして文章の最後にある "2001" という数字はただの数字ではなく、この文脈の中では 82.0601% の確率で日付(DATE)の一部、つまり「年」であると判断しています。これもおそらく前後関係からの判断だと思いますが、実際正しいですよね。


そして、これら名詞の解析結果が以下の <entities> に記載されている内容で出力されています:
 
  <entities>
    <entity eid="-E0" type="PERSON" generic="0" class="SPC" level="NAM" subtype="OTHER" score="0.645765">
      <mentref mid="-M0">John Smith</mentref>
      <mentref mid="-M2">he</mentref>
    </entity>
    <entity eid="-E1" type="GPE" generic="0" class="SPC" level="NAM" subtype="OTHER" score="1">
      <mentref mid="-M1">New York</mentref>
    </entity>
    <entity eid="-E2" type="DATE" generic="0" class="SPC" level="NONE" subtype="OTHER" score="1">
      <mentref mid="-M3">2001</mentref>
    </entity>
  </entities>

更に、これらの各名詞同士の関係として <relations> に記載される関連性が出力されています:
 
  <relations version="KLUE2_cascaded:2011_10_25">
    <relation rid="-R1" type="residesIn" subtype="OTHER">
      <rel_entity_arg eid="-E0" argnum="1"/>
      <rel_entity_arg eid="-E1" argnum="2"/>
      <relmentions>
        <relmention rmid="-R1-1" score="0.525653" class="SPECIFIC" modality="ASSERTED" tense="UNSPECIFIED">
          <rel_mention_arg mid="-M0" argnum="1">John Smith</rel_mention_arg>
          <rel_mention_arg mid="-M1" argnum="2">New York</rel_mention_arg>
        </relmention>
      </relmentions>
    </relation>
  </relations>

eid の "-E0" と "-E1"、つまり "John Smith" と "New York" の間は 52.5653% の確率で "residesIn"、つまり「常設する/住んでいる」の関係がある、と判断しています。これは "living" あたりから判断しているのだと思いますが、文脈としても正しい判断ができていますね。。


今回は簡単なサンプルを使ったので、結果も比較的説明しやすいものでした。ただこの結果を見る限りでは、入力した文章は正しく解析され、各単語の関係性もかなり正しく分析してくれているようです。 Watson はアメリカのクイズ王に勝った時に「クイズ問題文章を解析して、何が問われているのかを分析した上でその答を導き出し、その確信度がある程度の確率を上回った時に回答する」という解説を見たのですが、その仕組みの一端を垣間見たような気がしますね。。


これだけのサービスが言語限定とはいえ、IBM Bluemix を通じて無料で提供されているのはなかなか凄いことだと思います。これだけの機能を自前で実装するにはかなり困難が伴うでしょうが、今は無料サービスとして、誰でも使える形で提供されていることになります。

こうなると日本語サービスの提供が待ち遠しいです。もちろん言語としての難易度は全然違うでしょうけど、






 

IBM Bluemix でベータ公開されている Watson Relationsship Extraction サービス(API)を使ってみました。


このサービスは IBM の人工知能サービス Watson の中で、特にテキスト解析を目的として提供されているものです。文章としてのテキストをインプットすると、その中に書かれている関係性を解析して返してくれる、というものです。入力データは非常にシンプルですが、その解析結果はかなり複雑なものになります。しかもこれが REST の API を通じて利用できる、というサービスです。 残念ながら現時点では日本語テキストには対応しておらず、英語およびスペイン語でベータ版サービスが提供されています。ベータ版ということもあるでしょうが、現在は無料で利用できます。


これがどんなものなのか、試しに使ってみたので、その様子を紹介します。

まず、このサービスを利用するには IBM Bluemix のアカウントが必要です。また IBM Bluemix アプリケーションを作成することになります。この辺り、詳しくはこちらのエントリを参照するなどしてください:
http://dotnsf.blog.jp/archives/1001093607.html


また Watson Relationship Extraction サービス自体の解説はこちらを参照いただくのがいいと思います:
https://www.ibm.com/smarterplanet/us/en/ibmwatson/developercloud/doc/sireapi/


では実際にサービスを使ってみます。IBM Bluemix にログインし、Watson Relationship Extraction サービスを作成します。Watson カテゴリの中にあるのでこれを選択します:
2015010800


このサービスを REST から利用するにはエンドポイントURL、ユーザー名、そしてパスワードが必要です。その情報はサービスの「資格情報の表示」と書かれた箇所をクリックして確認できます:
2015010801



特に以下の情報が必要になるのでメモっておきます(これらの情報を環境変数からダイナミックに取得することも可能ですが、コードを単純にしたいので予め用意しておきます):
- url: REST をコールするエンドポイントURL
- username:API を実行する際のユーザー名
- password: 同パスワード

以下ではこれらの内容が後述のようであったと仮定して説明し、コードを書いていきます:
url : https://gateway.watsonplatform.net/laser/service/api/v1/sire/XXXXXXXXXX
username: USERNAME
password: PASSWORD

では、まずコードを書く前に API の実際の挙動を確認してみます。ブラウザを起動し、url の値を使い、以下の内容をそのままアドレスバーに入れてアクセスします:
https://gateway.watsonplatform.net/laser/service/api/v1/sire/XXXXXXXXXX?sid=ie-en-news&rt=xml&txt=John+Smith+lives+in+New+York%2C+and+he+has+been+living+there+since+2001.

ちなみに、URL パラメータ内の txt の値は、実際にテキスト解析したい内容(この例では "John Smith lives in New York, and he has been living there since 2001.")を URL エンコードしたものになります。それ以外のパラメータでは sid で英語文書であること、rt で結果フォーマットを XML にするよう指定しています。


実際にアクセスすると、ユーザー名とパスワードを聞かれるので上記で取得した値を入れます:
2015010802


成功すると、以下の様な XML テキストが返され、ブラウザ画面内に表示されるはずです(Firefox での例です):
2015010803


この中身については別途紹介するつもりですが、とりあえず挙動は分かったと思います。後はこれをコードにするだけです。Java のスタンドアロンアプリケーションで、取得結果を画面に出力するだけであればこんな感じですかね:
import java.net.URLEncoder;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.methods.GetMethod;

public class Test1 {
  static String bre_server = "gateway.watsonplatform.net";
  static String bre_path = "laser/service/api/v1/sire/XXXXXXXXXX";
  static String bre_username = "USERNAME";
  static String bre_password = "PASSWORD";
  static String bre_sid = "ie-en-news";
	
  public static void main(String[] args) {
    try{
      String txt = "John Smith lives in New York, and he has been living there since 2001.";
			
      String auth = bre_username + ":" + bre_password;
      String req_body = "sid=" + bre_sid + "&rt=xml&txt=" + URLEncoder.encode( txt, "utf-8" );
			
      String bre_url = "https://" + auth + "@" + bre_server + "/" + bre_path + "?" + req_body;

      HttpClient client = new HttpClient();
      client.getState().setCredentials( 
        new AuthScope( "gateway.watsonplatform.net", 443 ), 
        new UsernamePasswordCredentials( bre_username, bre_password ) );
      GetMethod method = new GetMethod( bre_url );
      method.setDoAuthentication( true );

      int res_sc = client.executeMethod( method );
      String res_body = method.getResponseBodyAsString();
      System.out.println( res_body );
    }catch( Exception e ){
      e.printStackTrace();
    }
  }
}

実際には取得した XML をそのまま出力するのではなく、その内容をパースして取り出して・・・という処理をすることになると思います。またスタンドアロンアプリで使うことは稀で、実際はサーブレットの中などで使うことになると思いますが、そこは目的に応じて、ということで。


では、この出力結果にはどのような情報が含まれているのか、その内容は長くなりそうなので別のエントリにします(苦笑)。

(2015.01.09 追記)
続きはこちら


 

久しぶりに IBM Bluemix ネタを書いてみよう。

これまで「IBM Bluemix は CloudFoundry をベースとした PaaS 環境」と紹介してきました。これはこれで間違いではないのですが、Bluemix のサービスとして提供されている機能がどんどん進化していて、今では Docker コンテナもサポートされている、らしい(けど今だと見つからない??)。 とはいえ、ベースが CloudFoundry であることは変わっていません。

CloudFoundry 環境にアプリケーションをデプロイしようとすると、cf というコマンドラインツールを使います。これは IBM Bluemix でも変わりません。IBM Bluemix(CloudFoundry) を使ってアプリケーション開発をしたり、作ったアプリケーションをデプロイしたりする際の方法はいくつかありますが、やはり cf ツールを導入しておくと便利です。

というわけで、その cf ツールをインストールする手順を紹介します。以前は環境依存でややこしかったりしていた部分ですが、今はかなり楽になりました。ちなみに以下で紹介する内容は CentOS 6(64bit) を前提としていますが、ほとんど変わらないと思います。

まずはこちらのサイトへアクセス: https://github.com/cloudfoundry/cli/releases
2015010601


で、環境にあった CF の最新バージョンの "Installer" をクリックしてダウンロードします。CentOS(64bit) の場合であれば RedHat 64bit と書かれた所のをダウンロード:
2015010602


そしてダウンロードしたモジュールをインストールします。CentOS の場合は rpm ファイルがダウンロードできるので、以下のコマンドでインストールできます:
# rpm -ivh cf-cli_amd64.rpm

インストール後にバージョン確認コマンドを実行して、表示されればOK:
# cf -v
cf version 6.8.0-b15c536-2014-12-10T19:26:38+00:00










 

このページのトップヘ