Oct 29, 2017

RaspbianにTomcat+MySQL環境構築

続いてTomcatからMySQLが使えるよう、MySQL Connector/Jをインストールした。

■やったこと

  • aptitudeでlibmysql-javaをインストール
  • /var/lib/tomcat7/shared/に/usr/share/java/mysql-connector-java.jarへのシンボリックリンクを作成
    例:
    ln -s /usr/share/java/mysql-connector-java.jar /var/lib/tomcat7/shared/
  • /etc/tomcat7/catalina.propertiesの
    shared.loader=${catalina.home}/shared/classes,${catalina.home}/shared/*.jar
    の"home"を"base"に変更
  • Tomcatを再起動

See more ...

WriteBacks

Oct 28, 2017

Raspbianにmod_jk+Tomcatインストール

特に何に使ってる訳でもないが、このブログの過去の記事で何か書いたのが残ってるし、Tomcatは筆者のお気に入りなので、Raspbianで作り直した自宅サーバーにもインストールしておくことにした。

▪️やったこと
  • Tomcatのインストール
    • aptitudeでtomcat7とtomcat7-examplesをインストール
    • http://localhost:8080/にアクセスしてServlets examplesが何らか動くことを確認
    • 昔作ったRandomTable Servletを/var/lib/tomcat7/webapps/servlet/に配置し、
      Tomcatを再起動し、
      http://localhost:8080/servlet/RandomTableにアクセスして動くことを確認
  • mod_jkのインストール
    • aptitudeでlibapache2-mod-jkをインストール
    • /etc/libapache2-mod-jk/workers.propertiesを書き換える
      workers.tomcat_home=/usr/share/tomcat7
      workers.java_home=/usr/lib/jvm/jdk-8-oracle-arm32-vfp-hflt
    • Tomcatはport 8009を待ち受ける設定に変更
      (上記ファイルにて"ajp13_worker"がport 8009に接続する設定になっている為)
      /etc/tomcat7/server.xml の Connector port="8009" ... の行のコメントアウトを外してTomcatを再起動
    • /servletへのアクセスをTomcatに転送する設定
      /etc/apache2/mods-available/jk.confの<IfModule jk_module>に
          <VirtualHost *:80>
              JkMount /servlet/* ajp13_worker
          </VirtualHost>
      
      または
          JkMount /servlet/* ajp13_worker
          JkMountCopy All
      
      を追加

See more ...

WriteBacks

Oct 11, 2017

Wolfram CDF Playerは期待外れだった

図書館からMathematicaの入門書を借りて、夏に買ったRaspberry Pi 2のMathematicaを少し使ってみた。

一昔前のワークステーション並みの速度は出ていると思うが、確かに遅い。
筆者はMathematicaは25年くらい前に大学にあったNeXTで触ったのが最初だが、その当時を思い出した。(実際には当時はもっと遅かったのだろうが)
入門書を読みながら書いてたら入力行が200くらいになったノートブックを丸ごと再計算させたら、2分かかった。
一番気になるのは、スクロール動作が遅いことで、スクロール操作を終えてから少し遅れてページが動くことがあるので、スクロールバーを操作して思った位置で止められなかったり、思った所にカーソルを当てられないことがあるのが地味にストレスになる。
まあ、筆者は主にMaximaを使っており、Mathematicaはたまにしか使わないし、本格的に使うこともまず無いので、これで満足である。

今日までずっと、Wolfram CDF Playerは前身のMathematica Playerとは違って、ノートブックの数式の評価(計算)ができるようになったものと勘違いしており、どんな数式でもRaspberry PiのMathematicaで書いてパソコンのCDF Playerで計算すれば良いと思っていたが、そうではないようだ。CDF Playerでできるのは、インタラクティブなUIを操作して変数の数値を変えて、UI上の評価結果を更新することだけらしい。
これを利用して、CDF Playerに任意の数式を評価させる手段を作成した人もいるようだが、決して便利ではない。
CDF Playerは、ほぼMathematicaそのものでプログラムサイズが巨大な割に、そのほとんどの機能が容易には使えなくされている、残念な代物のようだ。

さらに、Raspberry PiのMathematica(11.0.1.0)では再生できるのに、CDF Player(ver 11.1.1.0 for Mac)では再生できないアニメーションがあった。
Raspberry PiのMathematicaで

sol = DSolve[{y''[t] == -9.8, y'[0] == 10, y[0] == 0}, y, t];
x[t_] := 2.5 t;
Animate[Graphics[{Red, Disk[{x[t], First[y[t] /. sol]}, 0.5]},
Frame -> True, PlotRange -> {{0, 5}, {0, 5}},
GridLines -> Automatic], {t, 0, 2}, AnimationRunning -> False]

を実行すると、

このようなアニメーションが実行されるが、その状態をCDFとして保存してCDF Playerで開くと、グラフ部分がエラー表示になり、マウスカーソルを当てると
座標{$CellContext`x[0.], $CellContext`y[0.]}が数値のペア、Scaled形式、またはOffset形式ではありません。

というエラーメッセージが表示される。

また、CDF Playerで表示されるエラーメッセージには別バージョンもあり、

sol2 = DSolve[{y''[t] == -9.8, y'[0] == v0, y[0] == 0}, y, t];
Animate[Plot[First[y[t] /. sol2 /. v0 -> v], {t, 0, 2},
PlotRange -> {0, 5}], {v, 5, 10}, AnimationRunning -> False]

の出力

も同様にCDF Playerではエラーになり、
ReplaceAll: {sol2} is neither a list of replacement rules nor a valid dispatch table, and so cannot be used for replacing.

と表示される。

しかし、その下で

Show[Graphics[{Red, Disk[{x[t], First[y[0] /. sol]} /. t -> 0, 0.5]},
Frame -> True, PlotRange -> {{0, 5}, {0, 5}},
GridLines -> Automatic]]

Show[Plot[First[y[t] /. sol2 /. v0 -> 10], {t, 0, 2},
PlotRange -> {0, 5}]]

を実行して表示した、それぞれのアニメーションの1フレームは、CDF Playerでも正常に表示されるので、少なくともsol2は"a list of replacement rules"として機能しており、よくわからない。
Webで検索しても、同じようなエラーが発生するという情報すら見つからず、お手上げである。

無料だから文句は言えないが、インタラクティブなコンテンツを再生できるのが売りのCDF Playerなのに、よりによってアニメーションがエラーになるとは、残念である。

See more ...

Posted at 22:44 in 数学 | WriteBacks (0)
WriteBacks

Sep 06, 2017

要件定義が先か、要求分析が先か?

筆者は就職して約20年、一貫してソフトウェア開発の業務に関わってきた。研究や試作、性能改善などの為のものではなく、要求仕様に従って開発して最後ば残バグを0件にする、言わば品質の作り込みと品質確保を目的とするものである。
そのようなソフトウェア開発では、開発プロセスの定義が必須である。筆者も、ある時は実開発者として開発プロセスに従って作業し、ある時は外注管理担当として開発プロセスに則っているかを点検し、ある時はリーダーとしてプロセス改善に取り組み、ある時は間接業務担当として開発プロセスが崩壊してデスマーチとなる様子を眺めてきた。設計やソースコードよりも開発プロセスを最も意識して会社人生を過ごしてきたと言って過言ではない。

複数の開発プロセスを見てきて、用語の違いや揺れはかなりあったが、大体、ウォーターフォールモデル(及びスパイラルモデルの1サイクル)の開発フェーズは次のように分けられると理解してきた。


要件定義
ユーザー/顧客の言葉で、ソフトウェアに求められる要件を記述する

要求分析
ソフトウェアの仕様が一意に解釈される、完成したソフトウェアが要求を満たすかどうかを客観的に評価できる、要求仕様書を作成する

システム設計/方式設計
全体のモジュール分割、モジュールのI/F仕様(静的構造)やタスク分割(動的構造)、メモリ割り当てなど、ソフトウェア全体のトップレベルの基本設計を、プログラミング言語に依存しないレベルで行う

プログラム設計/モジュール設計/詳細設計
プログラミング言語に依存するレベルの設計やモジュールの内部設計を行う

実装
プログラムのコーディングを行う

単体テスト/モジュールテスト
モジュール内部の実装を踏まえた、網羅的なテストを行う
プログラム設計/モジュール設計/詳細設計に対応する

結合テスト
結合相手のモジュールがI/F仕様を満たしているかなどをテストする
システム設計/方式設計に対応する

システムテスト
ソフトウェア全体が要求仕様を満たしているかどうかをテストする

このように理解してきて、これまで実務上不都合はほとんど無かった。

たまに「要求分析」が「要件定義」より先になっている開発プロセスを見かけたが、たまたま、一般的な何かに基づいて作成されたように見えない、特定の会社が独自に考えたように見えるものばかりだったので、それを作った人の理解不足、きっとユーザーの「要求」を「分析」してシステムの「要件」を「定義」するという発想で字面だけで決めたものだろうと思っていた。

しかし、特にここ3年くらい、様々な企業が作成する資料で、「要求分析」が「要件定義」より先になっている開発プロセスをいくつも目にするようになって、気になるようになった。

そこで、ちょっとソフトウェア開発プロセスの「要件定義」と「要求分析」の使われ方について調べてみた。

[事例1]https://www.ipa.go.jp/files/000053941.pdf
IPA(情報処理推進機構)のサイトにある、この資料でも、「要求分析」→「要件定義」の順になっている。さらに「要求分析」→「要求定義」→「要件分析」→「要件定義」とも書かれていて、この一連の活動の成果物が要件定義書となっている。
要求分析は「顧客の要求」を分析するフェーズで、要件定義がシステムの要件を定義するフェーズという意図らしい。

[事例2]ESPR Ver.2.0
IPA/SECが作成した、ESPR(組込みソフトウェア向け開発プロセスガイド) Ver.2.0では「システム要求定義」「ソフトウェア要求定義」という言葉を使っている。開始条件として、それぞれ「製品企画として、エンドユーザーニーズが明確になっている」「製品仕様として、取説レベルの内容は決まっている前提になっている」とあり、出力はそれぞれシステム要求仕様書/ソフトウェア要求仕様書である。

[事例3]https://www.ogis-ri.co.jp/otc/hiroba/technical/RequirementsAnalysis/pdf/RequirementsAnalysis.pdf
オージス総研のサイトにある、この2007年くらいの記事では、「要求定義」の成果物が要求仕様書と書かれており、「要求定義」が要求分析を含むものと解釈できる。

[事例4]http://isw3.naist.jp/IS/TechReport/report/2011002.pdf
このレポートには「要求分析」の後に「要件定義」のように見える図があるが、本文は「要件定義」が「要件分析」/「要求分析」を含む前提で書かれている。

[事例5]JIS X 0160
ISO/IEC 15288の「利害関係者要件定義」→「要件分析」が、JIS X 0160では「利害関係者要求事項定義」→「システム要求事項分析」に対応すると書かれている。
「システム要求事項分析」の中に「要求事項の仕様化」があり、要求仕様書という言葉は使われていないが、次のフェーズが方式設計なので、このフェーズで要求仕様書が作成されるものと思われる。

[事例6]PMBOK(5th Edition)
"Collect Requirements"の次が"Define Scope"となっている。"Define Scope"において、specificationの類の単語は出て来ない。

[事例7]SWEBOK V3.0
"Requirements Elicitation"(要求抽出)
"Requirements Analysis"(要求分析)
"Requirements Specification"(要求仕様記述)
の順になっている。

[事例8]SDEM
よく知られた富士通のソフトウェア開発プロセスであるSDEMでは、「要件定義」という言葉が使われ、このフェーズが要求分析や要求仕様書作成を含むようである。

まず、「要求定義」や「要件分析」といった用語の揺れがあるが、筆者の理解では、開発プロセスの文脈において「要件」と「要求」の違いはほとんど無い。日本語的には、「要件」はシステムが満たすべきもので「要求」はユーザーが求めるものという印象があるが、元々はどちらも英語のrequirementを訳したものであり、「要件」=「要求仕様」だと思っている。「要件」は"requisite"だ、と書かれたのを何かで読んだことがあるが、筆者は英語の文献で"requisite"を目にしたことがない。
通常、"requirement"の訳は「要求」なので、表記の統一を目指せば「要件定義」は「要求定義」になるだろうが、筆者としては「要件定義」の方が日本語として自然だと思うし、Googleで調べても圧倒的に多い。

[事例1]では「要求分析」→「要求定義」→「要件分析」→「要件定義」となっているが、しかもこの一連のフェーズの成果物が要求仕様書の前段階の要件定義書だと、果たしてこれで要求分析と要件定義は明確に区別できるのだろうか?
筆者の感覚では、システムの動作仕様が一意に解釈される仕様書を作成するフェーズより前のフェーズはいくら分解しても無意味だと思う。そもそも分解したフェーズの完了基準を明確に一般化できないであろう。抽象レベルに差をつけるのであれば、結論ありきで逆算して段階的に抽象化する、無意味な工数を発生させるだけだと思う。

[事例6]も、日本語訳すれば"Collect Requirements"は「要求分析」、"Define Scope"は「要件定義」という感じなので、「要求分析」→「要件定義」であるが、同様に"Define Scope"のoutputが仕様書ではなく「要件定義書」という感じで、その先にも仕様を作成するフェーズが見当たらないので、そのままソフトウェアの開発プロセスとしては使えない。

[事例2][事例5]は要求仕様書を作成するフェーズの前にもユーザーの要求を扱うフェーズがあって、「要求定義」「要件分析」「システム要求事項分析」というフェーズで要求仕様書を作成している。

[事例3][事例4][事例8]はユーザーの要求を扱うフェーズが1つで、その大括りな「要求定義」「要件定義」というフェーズで要求分析を行い、要求仕様書を作成する。

[事例7]は要求仕様書を作成するフェーズの前に「要求分析」のフェーズがあるので、「要求分析」の成果物は、要求仕様書ではなく、要件定義書に相当するものだと思う。
ただ、少なくとも"Requirements Specification"を「要件定義」と訳す人はいないと思うので、[事例3][事例4][事例8]と同様、これらを含む大括りなフェーズの中に「要件分析」と「要求仕様書記述」があると解釈するのが妥当だろう。

こうして見ると、「要求分析」→「要件定義」の順の開発プロセスでは大体要求仕様作成、つまりソフトウェアの設計フェーズへのインプット作成まで行っていないので、何かソフトウェア開発とは異なるものを対象にしているのかも知れない。
もし「要件定義」という名前のフェーズで要求仕様書を作成するのであれば、名前からしておそらくそのフェーズで要求仕様書の前段階である要件定義書に相当するものも作成するだろうから、「要件定義」の前に似たような目的のフェーズは必要ないと思う。

筆者が冒頭で示したような、要件定義書作成フェーズと要求仕様書作成フェーズが大きく分かれているプロセスは、[事例2][事例5]のように近いものがいくつかあったが、要求仕様書を作成するフェーズの名称として「要求分析」が使われているケースは、上記の事例以外も含めて1つも見つからなかったので、「要件定義」→「要求分析」も一般的ではないようだ。筆者が最初の10年居た会社で外注管理の為に読みまくった開発プロセスには間違いなく「要求分析」と書かれていたので、個人的にショックである。そういえば、確か過去に要求仕様書作成フェーズの名前を「要求仕様」としていた開発プロセスがあり、当時はネーミングセンスが無いなあと思っていたが、今改めて考えると、よく体を表している名前だ。

最も一般的なのは、[事例3][事例4][事例7][事例8]のように、要求仕様書作成をゴールとする要件定義フェーズが要求分析を含む開発プロセスのようだ。確かに、要求仕様書作成に限らず、その前の要件定義書を作成する上でも要求分析と呼ぶべき作業は必ずあるので、不自然さが無く、これが妥当だと思った。

See more ...

Posted at 23:33 in PC一般 | WriteBacks (0)
WriteBacks

Aug 20, 2017

Raspberry Pi + Raspbian OSで試験運用中

このweblogは9年もの間、玄箱HGの自宅サーバーで運用して来たが、昨年辺りからHDDがすごい音を出すようになり、そろそろハードウェア的に限界が来ていると思っていたのと、少し前にこの玄箱を木製の密閉式のサイドボードに入れるようにしてから、えらく高温になり、その内発火しそうなので、サーバーを引っ越すことにした。

当初、24h運転の自宅サーバーを設置した理由は、weblogを始めようと思ったら、プロバイダーの無料のホームページ設置スペースではMovableType等のCMSが使えなかったからであり、その後、Javaアプレットを公開したり、色々なCGIやJavaのサーブレットを動かして遊ぶようになって手放せなくなったが、現在はそういったことまでできる、自由が効く安価なレンタルサーバーがあるので、自宅サーバーにこだわる理由が無くなった。

しかし、今回はRaspberry Piを購入して、自宅サーバーの運用を継続することに決めた。
あの高価な、少し前まで40万超えだったMathematicaを無料で使えるということで、いつかRaspberry Piを買おうと思っていたのだが、さらに先月、Raspberry Piで個人的に思い入れのあるFreeBSDが動作することを知り、この夏休みにFreeBSDでサーバー構築すべく、Raspberry Piを購入したのである。

最新のRaspberry Pi 3はFreeBSDが未対応ということなので、Raspberry Pi 2を購入した。
microSDカードは、相性があるそうでよくわからなかったので、とりあえず日本製であれば何でも良いかと思って、Amazonで安かったTOSHIBAのMSDAR40N32Gを合わせて購入した。

とりあえず動かしてみようと、セットアップマニュアルに従って、microSDに"NOOBS"を焼き込み、HDMIケーブルでテレビと繋ぎ、電源端子にmicroUSBケーブルを挿してUSB給電可能な玄箱HGを繋ぐと、セットアップ画面が表示された。
OSの選択をしないと先に進まないようだったので、USBマウスを繋いでRaspbian OSを選択すると、1時間くらいしてOSのインストールが終わり、再起動するとRaspbianのデスクトップ画面が開いて、上部のMathematicaアイコンをクリックするとMathematicaが起動したので、とりあえず感動しておいた。

続いて、FreeBSD-11.1-RELEASE-arm-armv6-RPI2.img.xzをダウンロードして、このページに従ってmicroSDに焼き込んで起動すると、テレビにカラフルな四角形が表示されたのだが、SDカードのアクセスランプが点滅を繰り返すばかりで、そこから先に進まなかった。
何がおかしいのだろうと調べると、Raspberry Pi 2 v1.2ではSoCがBCM2836からBCM2837に変更され、ARMプロセッサーがCortex-A7からCortex-A53に変わったので、同じカーネルでは起動できなくなったらしい。
せっかくPi 3より少し割高だったPi 2を買ったのに、ガッカリである。

大量に出回っているPi 3用のFreeBSDはその内正式リリースされるだろうが、主に既にPi 2を使ってるシステムの為に販売されるPi 2 v1.2向けにもリリースされるとは限らないので、FreeBSDのリリースを待たず、Raspbianでサーバー構築することにした。

まだまだ不完全ながら、夏休みを丸々費やして、とりあえずweblogのデータを引っ越して最低限動作するようになったので、Webサーバーをこれに置き換えた。

以下、やったことを大まかに控えておく。

1. VNCの有効化
 最初からRealVNCサーバーがインストールされており、デスクトップの「設定」→「Raspberry Piの設定」→「インターフェース」で有効化するだけだった。

2. aptitudeでmovabletype, apache2, mysql-serverをインストール
 まずupdateする必要があった。
 aptのパッケージにmovabletype 5.2.7があったので、これまでマニュアルインストールして使っていた3.3.5から移行することにした。今はWordPressが圧倒的に人気で、MovableTypeはほとんど使われなくなっているそうだが、MovableTypeはHTMLを静的に生成できるので、依然としてスペックの低いマシンに向いていると思う。
 movabletypeのインストール中に、設定ファイルを自動生成するか何かのダイアログが出たが、自動設定しないを選択した。

3. Apache2の設定
 /etc/apache2/sites-enabled/000-default.conf のDocumentRootを変更
 /etc/apache2/mods-enabled/ にcgid.conf, cgid.loadのsymlink追加
 /etc/apache2/conf-enabled/serve-cgi-bin.conf にアクセス可能アドレス設定

4. MovableTypeの試運転
 mt.cgiにアクセスすると

You don't have permission to access /XXX/mt-wizard.cgi on this server.

 というエラーになるので、
 /etc/apache2/conf-enabled/movabletype-opensource.conf
 の
Require all denied

 の行をコメントアウトしてmt_wizard.cgiを有効にする必要があった。
 (/usr/share/doc/movabletype-opensource参照)

 それから、何故か/usr/lib/cgi-bin/movabletype/にthemesへのsymlinkが無かったのを修正した。

5. MovableTypeのデータベースの移行
 MySQLのデータベースをimportした後、
 https://www.movabletype.jp/documentation/mt5/upgrade/
 の「既存のデータベースを上書きする方式」に従ってアップグレードした。

6. BlogのDesign Themeを"Rainier"に変更
 デフォルトのデザインがもう1つな感じがしたので、定番の1つらしい"Rainier"をインストールした。
 MT5.2対応と書いてあるが、html_head.mtmlの <$mt:StatsSnippet$>がMT6.0以降にしか無いので、コメントアウトする必要があった。
 それから、本文と追記の境界がわからなかったので、間に線を引くようにした。

7. MT-Keystrokes v0.1.5の導入
 keystrokes.plを/usr/share/movabletype/plugins/にコピー
 keystrokes.plは以下の2行をコメントアウト
 ・"return 1 unless $MT::VERSION =~ m(^3\.);"の行
 ・"$eh->error("keystroke id: " . $obj->blog_id);"の行
 MovableTypeの管理画面でテンプレートのComment Formを以下のように変更
 ・<form>タグの直後に<$MTKeystrokes$>を追加
 ・<textarea>タグ内にonkeypress="keystrokes(this.form)"を追加
 ・<input type="submit" value="Submit">タグ(2ヶ所)内にonclick="keystrokes(this.form)"を追加

8. ローカルメールサーバー立ち上げ
 aptでmovabletypeをインストールした時にexim4がインストールされたので、SMTPサーバーはこれを使用した。
 dpkg-reconfigure exim4-configで設定画面を開き、適当に設定した。
 POP3サーバーはDovecotをインストールした。

9. 固定IP化
 /etc/dhcpcd.confの末尾に次の4行を挿入

interface eth0
static ip_address=192.168.0.XX/24
static routers=192.168.0.1
static domain_name_servers=192.168.0.1

 (man dhcpcd.confのstaticの所参照)

See more ...

WriteBacks

Aug 10, 2017

野球のポジション当てゲームをプログラムで解いてみた

一昨日、野球のポジション当てゲームというのを知った。

http://heart-quake.com/article.php?p=527より:

1.藤山選手はサードと同じアパートに住んでいるということだ。
2.センターはライトより背は高いが、足はライトの方が早い。
3.鈴木選手の妹さんはセカンドと婚約中だそうで、どうも挙式は来春だそうだ。
4.キャッチャーの長男とサードの次女は同じ小学校の同級生だそうだ。
5.ショートとサードそれに桜井選手の3人はよく揃って競馬に行くそうだ。
6.ピッチャーはとても麻雀が強く、今月も梅田選手と菊池選手から5000円ずつまきあげたそうだ。
7.外野選手のうち一人はどうも木下選手か松村選手らしい。
8.小川選手はどうも奥さんとうまくいっていないようだ。近々離婚するのではないかとの噂がとんでいる。
9.選手達はよく揃ってゴルフに行くが、梅田・藤山・桜井の3選手はどうしてもキャッチャーとセカンドには勝てないようだ。
10.ピッチャーの奥さんはサードの妹さんだそうだ。
11.松村選手はキャッチャーと、又、桜井選手はピッチャーととても仲が良いようである。
12.選手たちのうちで、独身なのは、鈴木・梅田・山田の3選手、それにセンターとライトの5人である。
13.山田選手は桜井選手より背が高く、木下選手は桜井選手より背が低い。しかし、この3人はいずれもファーストより低い。

ここからは、問題文を一部改変しております。
製品をご購入いただくと正式な問題文が閲覧可能です。

14.選手たちのうちで酒を飲まないのは、XXX選手とXXX選手。それにショートの3人だけだそうだ。
15.バッテリーと内野の全員はXXX・XXX・XXXの3選手を除くとみんな外野の小川選手より背が低い。
16.鈴木選手は外野手のX人と一緒に麻雀をよくするそうだ。
こういう論理パズルで、色々制約のあるグループワークで解くもので、何らかの研修とかで使われるものらしい。

これが、色々制約のあるグループワークだから難しいが、一人でやったら簡単に解けるということだったので、上記XXXの部分を入手して、早速その日の夜にやってみたら、1時間くらいかけても解けなかった。

次の日の夜も、前日と同様、「藤山 not 5, 8 > 9, 鈴木 not 4, 4 single, ...」と紙に書き出すと、読み易い文字で書き直した為か、20分くらいで何らか解が出たのだが、解は複数あるような感じで、出した解は別途入手した答えと違った。

本当に解は1つしかないのだろうか?と気になって、今日、次のような簡単なプログラムを書いて探させてみたら、確かに解は1つしか無かった。

#!/opt/local/bin/python3
from itertools import permutations
names = ('藤山', '鈴木', '桜井', '梅田', '菊池', '木下', '松村', '小川', '山田')

def taller(name1, name2):
    dict1 = {'山田':3, '桜井':2, '木下':1}	#13
    if name1 in dict1 and name2 in dict1:
        if dict1[name1] > dict1[name2]: return True
    if name1 in ['XXX', 'XXX', 'XXX'] and name2 == '小川': return True	#15
    return False

def single(name):
    return name in ['鈴木', '梅田', '山田']	#12

def married(name):
    return name in ['小川']	#8

def judge(position):
    name_by_pos = {v:k for k,v in position.items()}

    if position['藤山'] == 5: return False	#1
    if taller(name_by_pos[9], name_by_pos[8]): return False	#2
    if position['鈴木'] == 4 or married(name_by_pos[4]): return False	#3
    if single(name_by_pos[2]) or single(name_by_pos[5]): return False	#4
    if position['桜井'] == 5 or position['桜井'] == 6: return False	#5
    if position['梅田'] == 1 or position['菊池'] == 1: return False	#6
    if (position['木下'] >= 7 and position['松村'] >= 7) \
       or (position['木下'] <= 6 and position['松村'] <= 6): return False	#7
    #8
    if position['梅田'] in (2,4) or position['藤山'] in (2,4) \
       or position['桜井'] in (2,4): return False	#9
    if single(name_by_pos[1]): return False	#10
    if position['松村'] == 2 or position['桜井'] == 1: return False	#11
    if position['鈴木'] >= 8 or position['梅田'] >= 8 or position['山田'] >= 8 \
       or married(name_by_pos[8]) or married(name_by_pos[9]): return False	#12
    if position['山田'] == 3 or position['桜井'] == 3 \
       or position['木下'] == 3: return False	#13
    if position['XXX'] == 6 or position['XXX'] == 6: return False	#14
    if position['XXX'] >= 7 or position['XXX'] >= 7 or position['XXX'] >= 7 \
       or position['小川'] <= 6: return False	#15
    if XXXXXXXXXXXXXXXXXXXXX: return False	#16
    return True

for p in permutations(range(9)):
    position = {names[i]: p[i]+1 for i in range(9)}
    if judge(position) == True:
        print(position)
(上記問題文で伏字にされている部分がわかる部分は伏字にしている)

プログラムのデバッグ中に、次のような表を作ってこれを埋めていく形で解き直したら、10分もかからずに解けた。問題を半分くらい覚えてしまったから早く解けたということもあるが、最初からこうやれば良かったと思った。

藤山 0 00
鈴木00 00
桜井00 000
梅田00 00
菊池0
木下 0 777
松村 0 777
小川 0 00
山田000 0
(1.〜13.まで読んでマークした状態、0は偽、7は7.参照の意)

See more ...

Posted at 21:06 in 数学 | WriteBacks (0)
WriteBacks

Jul 22, 2017

4五歩早仕掛けの玉頭銀対策を考える

筆者はこの所、4五歩早仕掛けを多用している。長らく将棋の勉強を怠っており、対四間飛車の急戦はこれしか思い出せないのである。しかも、四間飛車に対して急戦の構えを見せると△4三銀と上がる人が多いので、実現しやすく、便利である。
図1:4五歩早仕掛け

今月、久々に将棋を指す機会があり、久々の4五歩早仕掛けがクリーンヒットして安心し、その次の対局でもまた4五歩早仕掛けをしたら、4三の銀を5四〜6五〜7六と斜めにスルスルと動かされ、対処できずボロボロに負けてしまった。
図2:6九金型の4五歩早仕掛けに対する玉頭銀

筆者は昔からこの△5四銀が苦手なのであるが、寡聞にして定跡書で見たことが無かった為、この手は無い手で、必ず咎められる手だと思っていた。それで、△5四銀とされれば▲5五歩から殺しにかかるのだが、成功することは5回に1回もない。(しかも、銀を殺せても良くなるとは限らない。)

玉頭銀を殺すイメージ図
図3:歩を入手して▲7七歩とできれば銀が死ぬ

図4

図5:次に▲6六歩で銀が死ぬ

図6:このままなら次に▲7七歩で銀が死ぬ

今回も銀を殺せず、悪形だけが残り、総崩れしてしまった。
あまりにもひどかったので、一度きちんと調べておくことにした。



【結論】

  • この△5四銀〜△6五銀〜△7六銀と動く戦法には「玉頭銀」という名前が付いているらしい。
  • 玉頭銀は6九金型の4五歩早仕掛けに対して特に有効とされているらしい。
  • 4五歩早仕掛けの1手前の▲4六歩は△5四歩を待ってからにするべきらしい。
  • 従って、▲6八金直△5四歩の交換を入れてからにするべきらしい。


しかし、▲6八金直の次に△5四歩とされるとは限らない。
▲6八金直の直後に△5四銀と玉頭銀で来られれば▲5五歩△6五銀▲7五歩〜▲2六飛の筋があるし、▲3五歩△同歩▲4六銀が速いので、玉頭銀の牽制になっているとは思うが、後手には△1二香や△6四歩など、色々な手がある。△6四歩〜△5四銀〜△6三銀引と堅められることも多い。それに対して、▲6八金直は有効な待ちなのだろうか?

筆者は人生で▲6八金直にいい思い出が無い。△8四桂〜△7六桂の両取りを狙われるし、下段が空くし、5七銀の引き場所が無くなるし、▲5九香の頑張りがあまり利かなくなる。

▲6八金直が得な手でなく、次に△5四歩や△5四銀が期待できないなら、やっぱり▲6八金直と待つのではなく、さっさと▲4六歩と突きたい。それでもし△5四銀を食らっても、正確に指されれば少し悪くなる、という程度に済ませる方法は無いだろうか、と思って、懲りずに6九金型で玉頭銀を食らった場合の対策を探してみた。

△5四銀の後は(1)▲3七桂、(2)▲3八飛、(3)▲5五歩の3通りが多いようである。

(1)▲3七桂の後は、△6五銀▲4五歩△7六銀▲2四歩△同歩▲4四歩
図7
△同角▲同角△同飛▲7七歩△8七銀成
図8
▲同玉△8四飛▲8六銀△6四角▲9七角△9五歩▲同歩△9六歩▲同玉△8六角▲同角△8五銀
図9
と玉頭銀の恐ろしさを一方的に見せつけられるのが、代表的な進行である。

▲4四歩△4四同角には▲7七歩が正解だと思われる。▲4四歩に△3五歩でも▲7七歩と打つことになるらしい。(1)の▲3七桂は、銀を取れないのに形悪く▲7七歩を打つことになることが多く、いまいちである。

ただ、▲3七桂△6五銀▲4五歩△7六銀の後、▲4四歩でなく▲4四角とする手があるらしい。
△同角▲同歩△同飛なら▲6六角として、△8四飛と回るのを防げる。
図10

(2)▲3八飛は、△3二飛なら▲5五歩△6五銀▲3五歩△同歩▲同飛△7六銀▲2四歩△同歩▲5四歩(図6)のように銀を狙えそうだが、図6からでも簡単には銀が取れない。△4五歩でも助かりそうだし、△6四歩▲6六歩△6五歩▲8六歩△6六歩▲同銀△6四歩とやっても後手がなんとかなりそうである。

▲3八飛には△4五歩とする人が多いと思う。その後、▲3三角成△同桂▲8八角△4三金▲2八飛に△6四角(山崎六段)
図11
という手があり、これは先手こらえるのが大変そうである。△6四角に▲2四歩と攻め合って先手良しとする本もあるが、筆者には先手良しと理解できなかった。

▲3八飛には何と△4三銀と戻る手もあり、▲3五歩△同歩▲同飛△3二飛▲4五歩△同歩▲4四歩△2二角▲4五飛
図12
と自然に進めても、△3四銀(谷川-藤井)でうまくいかない。

やはり筆者としては、△5四銀には勝ち負けを度外視してでも(3)▲5五歩としたい。
▲5五歩の後は△6五銀▲3五歩△同歩▲3八飛
図13
とする。
そこで△4三金だと、▲3五飛△3四歩▲3六飛△7六銀▲4五歩△6五銀▲7五歩△4五歩▲8六飛(図5)のように銀挟みが成功する筋がある。

図13から△4五歩だと、▲3五飛△4六歩▲4五歩△3四歩▲同飛△4五飛▲3七桂△4四飛▲同飛△同角▲4一飛
図14
が一例で、ここで△3五角なら後手良しらしいが、アマ同士なら先手もやれそうではないだろうか。

図13から△4五歩▲3五飛△4六歩▲4五歩に△7六銀とした実戦例(森下-櫛田)もあり、▲4六銀△9五歩▲同歩△9八歩▲同香△9七歩▲同香△9六歩▲同香△8五銀
図15
と端から大暴れされたが、▲3二歩の垂らしから、と金を作って先手が勝っている。

図13から△7六銀だと、正確に指されると後手良しらしいが、▲3五飛△6五銀▲6六歩△7六銀▲5四歩△同歩▲9七角
図16
という指し方もあるかも知れない。△3二飛なら▲2四歩△同歩▲2二歩である。

1筋の突き合いが入っていると、6九金型でも玉頭銀に対して別の対策があるかも知れない。

1つには、△5四銀に対して、▲5五歩△6五銀▲3五歩△同歩▲3八飛△7六銀▲3五飛△6五銀に▲1五歩(升田-大山)という定跡がある。
図17
以下△同歩▲3四歩△2二角▲2四歩△4三飛▲5四歩△同銀▲3三歩成△同飛▲同飛成△同角▲2三歩成△5一角▲2八飛
図18
で先手優勢である。

もう1つは、△5四銀に対して、▲5五歩△6五銀▲3五歩△同歩▲3八飛△4五歩▲3五飛△4六歩の後、▲3七桂△7六銀▲4五桂が成立する。
図19
1筋の突き合いが入っていないと、ここで△1五角があり、▲5四歩△同歩▲1一角成としても△3四歩で後手良しになるが、図19だと△3四歩か△2二角しかなく、△3四歩なら▲3三桂成、△2二角なら▲2四歩でいずれも崩壊ではないだろうか。
 
問題はどこで1筋の突き合いを入れるかだが、▲5七銀左と上がる前に入れている実戦例が、上記の升田-大山戦を含め複数あるので、そのタイミングが良いと思う。ただ、後手が玉頭銀で来ない場合にどういう影響があるかは未確認である。

■参考文献
最強将棋21 四間飛車破り【急戦編】渡辺 明(著)

See more ...

Posted at 22:26 in 将棋 | WriteBacks (0)
WriteBacks

Jun 19, 2017

scikit-learnのナイーブベイズ分類器を使ってみる

前回のFamily Out Problemの確率モデル(下図)を題材に、ナイーブベイズ分類器を使ってみる。


Family Out Problem

この問題において、family-out以外の変数の真偽値が与えられた時にfamily-out=TRUEかどうかを判定するよう、ナイーブベイズ分類器を学習させてみる。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.naive_bayes import BernoulliNB
from sklearn.metrics import precision_recall_fscore_support
from sklearn.metrics import roc_curve, auc

def generate_sample():
    "Sample data generator of the Family Out problem"
    fo = np.random.binomial(1, 0.15)
    bp = np.random.binomial(1, 0.01)
    lo = np.random.binomial(1, (0.05, 0.6)[fo])
    do = np.random.binomial(1, ((0.3, 0.97), (0.9, 0.99))[fo][bp])
    hb = np.random.binomial(1, (0.01, 0.7)[do])
    return [fo, bp, lo, do, hb]

# Generate training data and test data
train_data = np.array([generate_sample() for _ in range(1000)])
test_data = np.array([generate_sample() for _ in range(100)])

X_train = train_data[:, 1:5]	# other than fo
y_train = train_data[:, 0]	# only fo
X_test = test_data[:, 1:5]	# other than fo
y_test = test_data[:, 0]	# only fo

# Train a Naive Bayes classifier
clf = BernoulliNB()
clf.fit(X_train, y_train)

# Evaluate with training data
y_pred = clf.predict(X_train)
metrics = precision_recall_fscore_support(y_train, y_pred)
print('Evaluation with training data')
print('Class FamilyOut=False: Precision={:.3f}, Recall={:.3f}, F-measure={:.3f}'.format(metrics[0][0], metrics[1][0], metrics[2][0]))
print('Class FamilyOut=True : Precision={:.3f}, Recall={:.3f}, F-measure={:.3f}'.format(metrics[0][1], metrics[1][1], metrics[2][1]))

# Evaluate with test data
y_pred = clf.predict(X_test)
metrics = precision_recall_fscore_support(y_test, y_pred)
print('Evaluation with test data')
print('Class FamilyOut=False: Precision={:.3f}, Recall={:.3f}, F-measure={:.3f}'.format(metrics[0][0], metrics[1][0], metrics[2][0]))
print('Class FamilyOut=True : Precision={:.3f}, Recall={:.3f}, F-measure={:.3f}'.format(metrics[0][1], metrics[1][1], metrics[2][1]))

# Draw ROC curve
y_train_post = clf.predict_proba(X_train)[:, 0]
y_test_post = clf.predict_proba(X_test)[:, 0]

fpr, tpr, thresholds = roc_curve(y_train, y_train_post, pos_label=0)
roc_auc = auc(fpr, tpr)
plt.plot(fpr, tpr, 'k-', lw=2, label='ROC for training data (area = {:.2f})'.format(roc_auc))

fpr, tpr, thresholds = roc_curve(y_test, y_test_post, pos_label=0)
roc_auc = auc(fpr, tpr)
plt.plot(fpr, tpr, 'k--', lw=2, label='ROC for test data (area = {:.2f})'.format(roc_auc))

plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC curve of BernoulliNB')
plt.legend(loc="lower right")

plt.show()

問題のモデルに従って学習用データとテストデータをランダムに生成し、sklearn.naive_bayes.BernoulliNBの分類器を学習させ、テストデータを分類させ、family-out=FALSEとfamily-out=TRUEのそれぞれについてPrecision, Recall, F値を計算している。
加えて、予測したfamily-out=FALSE/TRUEの確率から、ROC曲線とAUCを出力している。

出力例

Evaluation with training data
Class FamilyOut=False: Precision=0.936, Recall=0.983, F-measure=0.959
Class FamilyOut=True : Precision=0.847, Recall=0.589, F-measure=0.695
Evaluation with test data
Class FamilyOut=False: Precision=0.923, Recall=0.988, F-measure=0.955
Class FamilyOut=True : Precision=0.889, Recall=0.533, F-measure=0.667
ROC曲線
ROC curve
その時のtraining_dataとtest_data(.arff形式)
family_out_train.arff
family_out_test.arff

ナイーブベイズ分類器には、同じクラスのデータでは全ての特徴が独立に出現する、つまりbp,lo,do,hbに依存関係が無く、これらがTRUEになる確率はfoの値だけで決まるという仮定があるが、この問題ではbp,do,hbに依存関係があるので、ベイジアンネットワークの方がうまく学習できると考えられる。
なのでベイジアンネットワークで分類した場合の性能と比較したいが、scikit-learn(0.18)にベイジアンネットワークが無いので、代わりにWeka 3.8.1のBayesNetを用いて学習用データで学習させ、テストデータを分類した結果と比較してみる。

Wekaの操作手順

  1. Weka ExplorerのPreprocessタブでOpen file...ボタンを押し、family_out_train.arffを開く
  2. ClassifyタブでClassifierとしてBayesNetを選択する
  3. "BayesNet -D ..."とあるフィールドをクリックしてパラメーター設定画面を開き、searchAlgorithmのフィールドをクリックして、initAsNaiveBayes=False, maxNrOfParents=2に変更
  4. Test optionsのSupplied test setのボタンを押し、family_out_test.arffを開き、Classとしてfamily-outを選択
  5. クラスをfamily-outを選択し、Startボタンを押す
上記と同じデータを使った出力例
=== Run information ===

Scheme:       weka.classifiers.bayes.BayesNet -D -Q weka.classifiers.bayes.net.search.local.K2 -- -P 2 -N -S BAYES -E weka.classifiers.bayes.net.estimate.SimpleEstimator -- -A 0.5

=== Detailed Accuracy By Class ===

                 TP Rate  FP Rate  Precision  Recall   F-Measure  MCC      ROC Area  PRC Area  Class
                 0.988    0.467    0.923      0.988    0.955      0.651    0.889     0.967     0
                 0.533    0.012    0.889      0.533    0.667      0.651    0.889     0.648     1
Weighted Avg.    0.920    0.398    0.918      0.920    0.911      0.651    0.889     0.919     

=== Confusion Matrix ===

  a  b   <-- classified as
 84  1 |  a = 0
  7  8 |  b = 1

BernoulliNBのテストデータに対する出力と、Precision, Recall, F-measureが完全に一致している。

WekaのBayesNetのデフォルト設定では正しいネットワークが学習されなかったが、上記手順の3.のように設定を変えると、大体次のように正しいネットワークになった。

BernoulliNBの性能には試行毎にばらつきがあったが、1000回の平均を取ってみたのが次の値であり、上記の結果は特別に良い例ではない。

Average with training data
Class FamilyOut=False: Precision=0.925, Recall=0.982, F-measure=0.952
Class FamilyOut=True : Precision=0.844, Recall=0.545, F-measure=0.661
Average with test data
Class FamilyOut=False: Precision=0.925, Recall=0.982, F-measure=0.952
Class FamilyOut=True : Precision=0.842, Recall=0.544, F-measure=0.651
Average of AUC with training data=0.894
Average of AUC with test data=0.895
また、計10回、同じデータでWekaのBayesNetの性能と比較した所、Precision, Recall, F-measureについては、8回は一致していた。
ROC曲線のAUCはWekaのBayesNetの方が高いことが多かったが、BernoulliNBでも大体0.85-0.90の範囲であり、十分に高かった。
従って、この確率モデルに対して、ナイーブベイズ分類器は予測性能が十分に高いと言える。

なお、ここまでの結果では、学習データに対する各種性能値とテストデータに対する値にほぼ差が無いが、これは学習データのサンプル数が1000と十分に多く、偏りが無いからである。これを100にすると、次のように、テストデータに対する成績が少し下がるが、それでも、学習データだけに対して大幅に成績が良い「過学習(overfitting)」の状態と言える程ではない。

学習データ数が100の1000試行の平均

Average with training data
Class FamilyOut=False: Precision=0.931, Recall=0.963, F-measure=0.945
Class FamilyOut=True : Precision=0.793, Recall=0.582, F-measure=0.650
Average with test data
Class FamilyOut=False: Precision=0.926, Recall=0.955, F-measure=0.938
Class FamilyOut=True : Precision=0.767, Recall=0.564, F-measure=0.620
Average of AUC with training data=0.899
Average of AUC with test data=0.890

See more ...

Posted at 22:35 in 数学 | WriteBacks (0)
WriteBacks

May 29, 2017

Wekaでベイジアンネットワークの事後確率を計算

ベイジアンネットワークの復習をしていて、確率的グラフィカルモデル‐ベイジアンネットワークとその周辺‐(オペレーションズ・リサーチ 2013年4月号)という記事を見つけた。その中に、Family Out Problemという有名な例題(下図)の紹介があり、p.194に、

表1~3で与えられたCPTの値と式(11)を利用して丹念に計算することにより P(X1=1|X3=1,X5=1)=0.7577···という結論を得る.
と書かれていたので、これを自力で計算できたらベイジアンネットワークを理解できたことにしよう、と思って、丹念に計算してみたら、その値にならなかった。


Family Out Problem

ここでは、
X1: Family Out
X2: Bowel Problem
X3: Light On
X4: Dog Out
X5: Hear Bark
(それぞれ2値の確率変数)であり、それぞれの条件付き確率は次の通りである。
P(X1=1) = 0.15
P(X2=1) = 0.01
P(X3=1 | X1=0) = 0.05
P(X3=1 | X1=1) = 0.6
P(X4=1 | X1=0, X2=0) = 0.3
P(X4=1 | X1=0, X2=1) = 0.97
P(X4=1 | X1=1, X2=0) = 0.9
P(X4=1 | X1=1, X2=1) = 0.99
P(X5=1 | X4=0) = 0.01
P(X5=1 | X4=1) = 0.7

P(X_1=1 | X_3=1, X_5=1) = \frac{\sum_{X_2}\sum_{X_4} P(X_1=1, X_2, X_3=1, X_4, X_5=1)}{\sum_{X_1}\sum_{X_2}\sum_{X_4} P(X_1, X_2, X_3=1, X_4, X_5=1)}
なので、Pythonで

P00101 = 0.85 * 0.99 * 0.05 * 0.7 * 0.01
P00111 = 0.85 * 0.99 * 0.05 * 0.3 * 0.7
P01101 = 0.85 * 0.01 * 0.05 * 0.03 * 0.01
P01111 = 0.85 * 0.01 * 0.05 * 0.97 * 0.7
P10101 = 0.15 * 0.99 * 0.6 * 0.1 * 0.01
P10111 = 0.15 * 0.99 * 0.6 * 0.9 * 0.7
P11101 = 0.15 * 0.01 * 0.6 * 0.01 * 0.01
P11111 = 0.15 * 0.01 * 0.6 * 0.99 * 0.7

P35 = P00101 + P00111 + P01101 + P01111 + P10101 + P10111 + P11101 + P11111
P135 = P10101 + P10111 + P11101 + P11111
P1_35 = P135 / P35
print(P1_35)
とすると、0.8578...という数値になった。どこか読み間違えたかと思って、
def prob(x1, x2, x3, x4, x5):
    p = 1.0
    p *= (0.85, 0.15)[x1]
    p *= (0.99, 0.01)[x2]
    p *= ((0.95, 0.05), (0.4, 0.6))[x1][x3]
    p *= (((0.7, 0.3), (0.03, 0.97)), ((0.1, 0.9), (0.01, 0.99)))[x1][x2][x4]
    p *= ((0.99, 0.01), (0.3, 0.7))[x4][x5]
    return p

P135 = sum([prob(1, x2, 1, x4, 1)
             for x2 in range(2)
             for x4 in range(2)])
P35 =  sum([prob(x1, x2, 1, x4, 1)
             for x1 in range(2)
             for x2 in range(2)
             for x4 in range(2)])
P1_35 = P135 / P35
print(P1_35)
と書いてみたが、やはり0.8578...だった。

上記の記事内の条件付き確率表に誤記があり、0.7577...というのは元の確率表で計算された値か、とも思ったが、どの文書のFamily Out Problemを見ても確率は同じだった。

正解は何なのか、何らかのツールで確認しようと思って、Pythonのベイジアンネットワーク関連のツールを探したが、適当なものがなかなか見つからなかった。

scikit-learnには"Naive Bayes"のAPIはあるがベイジアンネットワークは見つからない。
PyMCBayesPyは、きっとうまく使えばこの計算ができるのだろうが、ネットワークを定義して、CPT(conditional probability table、条件付き確率表)とevidence(観測値)を与えて事後確率を計算する直接的なサンプルコードが見つからなかったので、諦めた。
PBNTにはそういうサンプルコードがあったので、使ってみたが、P(X1=1|X3=1,X5=1)=0.2018...という全然違う値が出力された。P(X5=1)=0.2831と、これは正しい値が出たので、ネットワークとCPTは合ってそうであり、事後確率を計算するにはengine.marginal()でなく別のメソッドを使わないといけないのかとも思ったが、よくわからなかった。

Pythonを諦めてツールを探すと、Wekaでできることがわかった。Wekaは機械学習の勉強をするなら必修らしく、過去にインストールしていたので、やってみた。
Wekaを起動して、Tools->Bayes net editorを開くと、GUIがバグだらけ(Version 3.8.1, WindowsとMacとで確認)で使いにくいが、ノードを追加して右クリックしながらネットワークを作成し、Tools->Layoutでノードの配置を修正し、CPTを設定し、さらに保存したXMLを書き換えて色々修正し、Tools->Show Marginsを選ぶと、次のように結合確率が表示される。

さらに、右クリック->Set evidenceで LightOn=True, HearBark=True と設定すると、次のように、各ノードの事後確率が表示される。

これによると、FamilyOut=Trueの事後確率はやはり0.8578...である。筆者は何か問題を読み間違えているのだろうか?

Posted at 18:31 in 数学 | WriteBacks (0)
WriteBacks

May 21, 2017

角交換振り飛車に大ポカ一発で沈む

昨日は毎年参加している地域の将棋大会だった。
今年は2回戦敗退だったが、予選は1勝1敗で抽選で勝ち抜け(3人のブロックで3人とも1勝1敗だった)、抽選で決勝1回戦はシードだったので、昨年の1回戦敗退より悪い内容だった。通常3勝かかる所、たった1勝で2回戦まで進出するとは、何とくじ運の良かったことか。

筆者はここ1年くらい将棋の勉強をしておらず、全て忘れてしまっており、先月からたまにネット将棋を指していたが以前のレートでは全く勝てず、今年は予選突破できないと思った。
昔から記憶力が無い方であるが、たった1年休んだくらいで全て忘れてしまい、レートが200も下がるのでは、筆者は将棋に向いてないのだろうとつくづく思う。

2回戦の相手は昨年優勝のI藤さんだった。筆者が絶好調でもまず勝てない相手であるが、先手の筆者に次の局面のような感触の良い手が出た。

通常は4二の銀が4四に居るので、3四の歩を取っても響かないが、この形だと歩を取った後に3筋の歩を伸ばせる。3四の歩を受けるには△1二角と打つしかないが、形が悪いだろう。
筆者は通常、角交換振り飛車には▲6六歩のようにして角交換を拒否するのだが、1回戦で筆者が100回やっても勝てそうにないY田さんがI藤さんに対して▲6六歩と角交換を拒否して負けたのを見て、直前に何か別の展開をガラケーの自作アプリに仕込んだ棋譜の中から1つ探して、その通りに指してみたものである。そういうことをすると通常は相手の研究にはまるので、ろくなことにならないのだが、今回は功を奏した。
対して、後手のIさんは△3二飛と指した。

これを見た瞬間、チャンスだと思った。2三の地点が空いている。
その誘惑に駆られた上、一手前に長考したこともあり、じっくり考えようとは思わなかった。
▲3四角に△4五桂と両取りに跳ねられても▲同角で両方受かる、と、それ以上考えずに、10秒も使わずに▲3四角と指したら、△2五桂とされて、一発で撃沈してしまった。

あと5秒考えていればこの手に気付いただろう。有段者が何の意味もなく、△3二飛のような隙を作る手を指すはずがない、と、何故一瞬でも思わなかったのか。
△2五桂の後、▲同歩、△3四飛、▲2四歩とすれば、後手は歩切れで、と金ができて大差にはならなさそうだが、この局面では丁度△1五角があって、受かってしまう。

△3二飛の局面は激指14のPro+3でも評価値が+280であり、少しリードしていたのは間違いない。その後、▲3四角ではなく、▲2九飛 △5四歩 ▲4八金と桂馬に紐を付け、△1二角 ▲6六歩 △6四歩 ▲1六歩のように進めれば、まあまあ指しやすそうである。

ここまでの手順は激指14のPro+以上の手を続けたものであり、評価値は+228である。

まあ、相手が相手なだけに、こう進んでもまず勝てなかっただろうが…

See more ...

Posted at 22:44 in 将棋 | WriteBacks (0)
WriteBacks

MacPortsでmaximaをインストールするとエラーになるのを回避

現在、MacPortsを使って

sudo port install maxima
すると、下のようなエラーになって失敗してしまう。(macOS Sierra 10.12.4で確認)
../../doc/info//errormessages.texi:296: `Warning messages' has no Up field (perhaps incorrect sectioning?).
../../doc/info//errormessages.texi:162: Prev field of node `Operators of arguments must all be the same' not pointed to.
../../doc/info//errormessages.texi:152: This node (Only symbols can be bound) has the bad Next.
../../doc/info//errormessages.texi:152: Next field of node `Only symbols can be bound' not pointed to (perhaps incorrect sectioning?).
../../doc/info//errormessages.texi:204: This node (out of memory) has the bad Prev.
../../doc/info//errormessages.texi:8: `Error messages' has no Up field (perhaps incorrect sectioning?).
makeinfo: Removing output file `/opt/local/var/macports/build/_opt_local_var_macports_sources_rsync.macports.org_macports_release_tarballs_ports_math_maxima/maxima/work/maxima-5.39.0/doc/info/maxima.info' due to errors; use --force to preserve.
make[3]: *** [maxima.info] Error 1

原因は、makeinfoのバージョンが古いからのようだ。
http://maxima-discuss.narkive.com/KeLR9qhb/can-t-install-5-39
に、makeinfoのバージョンが4.xだとこうなることが書かれている。 実際、macOS Sierraで makeinfo --version すると、(GNU texinfo) 4.8と表示される。

そこで、

sudo port install texinfo
(GNU texinfo 6.3のmakeinfoがインストールされる)してから再度
sudo port install maxima
すると、上記のエラーは出なくなった。Activationの段階で
--->  Installing maxima @5.39.0_3+xmaxima
--->  Activating maxima @5.39.0_3+xmaxima
Error: Failed to activate maxima: Image error: /opt/local/bin/maxima already exists and does not belong to a registered port.  Unable to activate port maxima. Use 'port -f activate maxima' to force the activation.
というエラーになるが、メッセージの通り、
sudo port -f activate maxima
とすると、一応wxMaximaと併用しても問題なく動いた。

See more ...

Posted at 21:22 in PC一般 | WriteBacks (0)
WriteBacks

May 06, 2017

JediにAutoCompleよりCompanyModeを優先させる

2ヶ月前にCarbon EmacsのPython環境を整備したばかりなのだが、MacのOSを10.7.5から10.12.4にバージョンアップすると、Carbon Emacsがまともに動かなくなってしまった。起動はするのだが、すぐに固まってしまう。しかも、その後distnotedというプロセスが全てのCPUを奪い続けて、Macが激重になる。
この現象はOS X 10.9+Emacs 24.3で起こるらしく、Carbon Emacs(Emacs 22)でも起こったという情報は見つけられなかったが、おそらく同じ問題である。どちらかと言うとOSのバグなのだが、現時点ではEmacsを24.4以降にバージョンアップする以外に解決方法が見当たらない。

その為、長年お世話になったCarbon Emacsを手放し、Emacsの最新版である25.2を使うことにした。

EmacsWikiのPython Programming In EmacsのページにはPythonの開発環境を便利にする方法が色々書かれているが、筆者は高機能なPythonの開発環境を使いたい時はSpyderを使っており、EmacsのPython環境はPython.elでPython3が使えて、もう少し便利な補完が効けば十分なので、Jediだけを追加インストールすることにした。

Emacs 25.2にbuilt-inのPython.elはPython3に対応しているのだが、MacPortsでpython35をインストールして、M-x customize-group -> pythonして"Python Shell Interpreter"をpython3にすると、Warningが出まくり、妙に不安定だった。M-x list-packagesしてpython packageを0.25.2にバージョンアップすると少し改善したが、完全には直らなかった。
さらに、Jediをインストールすると、これまた不安定で、時々補完候補が出る前にEmacsが固まった。(C-gで抜けることはできる)

Emacs 24の最新版である24.5.1ではこの問題は起こらなかったので、当面、EmacsでPythonのコードを書く時は24.5.1を使うことにした。

Jediのインストールは、

(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(package-initialize)
としてpackage.elにMELPAのリポジトリを登録し、M-x list-packagesしてjediをインストールし、Jedi.elのドキュメントに従ってセットアップした。

なお、Emacs25でjediをインストールすると、Emacs24で"Symbol's function is void: cl-struct-define"というエラーになった。これはjediに限らず、よくあることだそうで、Emacsの24と25を併用する場合、package.elで何かをインストールするならEmacs24でする方が良さそうだ。

さて、JediはAutoCompleteとCompanyModeの両方に対応しているが、package.elで"jedi"をインストールするとAutoCompleteが使われる。"company-jedi"をインストールするとCompanyModeが使われるのだが、"jedi"と"company-jedi"の両方をインストールすると、AutoCompleteが優先される。特にAutoCompleteに不満は無く、和製なので贔屓したいが、色々読んでいると、世界的には、日本でも現在は、CompanyModeの方が人気があるようなので、何が良いのかを知るために、これからしばらくはCompanyModeを積極的に使うべく、両方をインストールしてCompanyModeをデフォルトにすることに決めた。

しかし、.emacsや.emacs.d/init.elでCompanyModeを優先する適当な方法がわからなかった。Python modeにしてM-x auto-complete-modeとすればAutoCompleteのON/OFFが切り替わり、OFFだとCompanyModeが使われるのだが、Emacs Lispで(auto-complete-mode)としてもON/OFFが切り替わらず、auto-complete-mode関数にはOFFにする引数も無いのである。auto-complete-mode関数のソースコードを見ると、"(if auto-complete-mode ..."とあるので、

(setq auto-complete-mode nil)
(auto-complete-mode)
とすれば良さそうに思ったが、これでもOFFにならない。
但し、(setq auto-complete-mode nil)すると、即座にAutoCompleteがOFFになる。このことを使って、試行錯誤の末、.emacs等に
(add-hook 'python-mode-hook 'jedi:setup)
よりも前のどこかに
(add-hook 'python-mode-hook
(lambda () (setq auto-complete-mode nil)))
と書けば、Python mode開始時にAutoCompleteがOFFになり、CompanyModeが使われることがわかった。

See more ...

Posted at 23:32 in PC一般 | WriteBacks (0)
WriteBacks

Apr 09, 2017

ROC曲線を理解する

2値の予測(判別、識別、...)に用いる特徴量の良し悪しを評価する1つの方法として、ROC曲線というものがある。
実際は正で予測も正であるデータの数をTP(True Positive)、
実際は負で予測も負であるデータの数をTN(True Negative)、
実際は負で予測は正であるデータの数をFP(False Positive)、
実際は正で予測は負であるデータの数をFN(False Negative)、
と呼ぶ時、
TPR(True Positive Ratio)=TP/(TP+FN)を縦軸、
FPR(False Positive Ratio)=FP/(FP+TN)を横軸、
としたグラフである。

ROC曲線の例
ROC curve sample

ROC曲線は、必ず(0,0)から始まって(1,1)で終わる。
特徴量が全く予測の役に立たない、ランダムな値であれば、TPR=FPRの線になる。
ROC曲線より下の面積、AUR(Area Under ROC curve)(または単にAUC(Area Under the Curve))が大きいほど、特徴量の値の全域に渡って良い特徴量だとされる。 理想的な特徴量だと、ROC曲線はFPR=0とTPR=1の線になる。

ROC曲線は機械学習で識別器の評価によく用いられるらしいので、とりあえず覚えておこうと思ったのだが、筆者はこれの理解にえらく苦労したので、調べたことや考えたことをメモする。
統計学の検定と同様、こういう確率と論理を組み合わせたものは、人によって向き不向きがあるのだと思いたい。

TP,TN,FP,FNの関係を再度整理すると、次のようになる。

予測

TPFN (Type II error)
FP (Type I error)TN
PやNは予測がPositiveかNegativeかであり、TやFはそれが正解かどうかである。
FPは誤検出のことであり、統計学の検定でも使われる「第一種の過誤」(検定では帰無仮説を棄却できないのに棄却する条件に誤ってヒットしてしまうこと)である。
FNは検出不能であり、「第二種の過誤」(検定では帰無仮説が誤りなのに棄却する条件にヒットしないこと)である。

予測の精度に関する尺度としては、Accuracy, Presicision, Recall, F値があり、それぞれ次のように定義される。
Accuracy = (TP+TN) / (TP+TN+FP+FN)
予測の正解率。
Precision = TP / (TP+FP)
Positiveと予測される中の正解率。
Recall = TP / (TP+FN)
実際にPositiveの内、Positiveと予測される割合。検出力、Sensitivity。
F値(F-measure, F1 score) = ((Precision-1 + Recall-1)/2)-1
PrecisionとRecallの調和平均。統計学のF分布に従うF値とは関係ない。
PrecisionとRecallはトレードオフの関係にあるので、それらをバランス良く合成した尺度。

予測の精度はAccuracyで評価するのが簡単だが、実際の正のデータ数と負のデータ数に偏りがあると、データ数が少ない方の正解率が低くても、データ数が多い方の正解率が高ければAccuracyが高くなってしまうので、Accuracyだけでは適切に評価できない。
そのような場合にPrecisionやRecallが用いられるが、これらは一般に特徴量の閾値によってトレードオフの関係があり、セットで評価しないといけないので、単純比較には向かない。そこで用いられるスカラー値が、F値や、ROC曲線のAUCである。
Precision-Recall曲線のAUCも使われることがあるが、Presicionは実際の正のデータの割合に依存するので、正のデータの割合が同じでないと比較には使えない。実際の正のデータの割合が極端に小さい場合など、Precisionが大きな意味を持つ場合にはPrecision-Recall曲線が用いられる。

ROC曲線は、TPR=TP/(TP+FN)とFPR=FP/(FP+TN)のグラフである。TPRを陽性率、FPRを偽陽性率と呼ぶこともある。TPRはRecallと同じである。FPRはfall-out(副産物)と呼ばれることもある。

次の図の3つのROC曲線が、正のデータと負のデータがどのように分布する特徴量に対応するかを考えてみる。
ROC curve sample 2

例えば、次のような分布になる特徴量だと、青いROC曲線になる。
distribution of totally independent feature
横軸は特徴量、縦軸は赤い部分が正のデータの分布、青い部分が負のデータの分布を表している。このグラフでは、正のデータも負のデータも一様分布している。閾値tより右ならPositive、左ならNegativeと予測する時、tを右端から左に動かすと、TPRもFPRも0から1に向かって増大するが、常にTPR=FPRである。正のデータと負のデータの割合はグラフの形状には関係しない。

次のような、正のデータと負のデータが完全に分かれる理想的な特徴量だと、緑のROC曲線になる。
distribution of ideal feature
tを右端から左に動かすと、FPR=0のままTPRが0から1に変化し、青のゾーンに入ると、FPRが0から1に変化する。

次のような分布だと、赤いROC曲線になる。
distribution of which makes ROC curve perfect arc
tを右端から左へ動かすと、FPRよりもTPRの方が早く上昇する。なるべく正のデータと負のデータの分布が分離している良い特徴量ほどFPRが上昇する前にTPRが上昇するので、AUCが大きくなることがわかる。

AUCはどれくらいだと良いか、という基準は一般的なものも色々あるようだが、大体、最低0.7は無いと有効ではないとされるようである。

なお、特徴量の最良の閾値(cut-off)はROC曲線の(0,1)に最も近い点とする、という方法を複数の箇所で目にしたが、明確な理論的根拠がある訳ではなく、必ずしもそれに限定されないようである。そもそも、(0,1)に最も近いというのがユークリッド距離で良いのかどうかがわからない。

See more ...

Posted at 20:30 in PC一般 | WriteBacks (0)
WriteBacks

Mar 31, 2017

pandasでmergeせずにgroupbyで間接参照したい

この前、pandasを使っていて、次のような感じの、関連する2つのテーブル、access_logとchoice_logがある時に、結合したテーブルを作らずにchoice毎のtimestampの最小値を求めたかったのだが、どう書けば良いのかわからなかった。

import pandas as pd
import numpy as np
access_log = pd.DataFrame({'session': [100, 101, 102, 103, 104, 105, 106, 107, 108, 109],
                           'timestamp': [314, 159, 265, 358, 979, 323, 846, 264, 338, 327]})
choice_log = pd.DataFrame({'session': [100, 100, 101, 102, 102, 103, 104, 104, 105, 106, 106, 107, 108, 108, 109],
                           'choice':  ['A', 'B', 'C', 'D', 'E', 'A', 'B', 'C', 'D', 'E', 'A', 'B', 'C', 'D', 'E']})
>>> access_log
   session  timestamp
0      100        314
1      101        159
2      102        265
3      103        358
4      104        979
5      105        323
6      106        846
7      107        264
8      108        338
9      109        327
>>> choice_log
   choice  session
0       A      100
1       B      100
2       C      101
3       D      102
4       E      102
5       A      103
6       B      104
7       C      104
8       D      105
9       E      106
10      A      106
11      B      107
12      C      108
13      D      108
14      E      109
>>> 

結合テーブルを作るなら、次のように書ける。

merged = choice_log.merge(access_log, on='session', how='left')
result = merged.groupby('choice')['timestamp'].min()
>>> result
choice
A    314
B    264
C    159
D    265
E    265
Name: timestamp, dtype: int64
>>> 

実際にあったテーブルは巨大で、他にも列がたくさんあり、単純に結合テーブルを作るとRAMが足りなくなってメモリスワップが多発したので、結合テーブルを作らずにこれと同じことがしたかったのだが、その書き方がわからなかった。
結局access_log.set_index('session').to_dict()のようにして一時的なdictを作って、スワップを多発させながら処理してしまった。
それが心残りだったので、改めてpandasのドキュメントを拾い読みしながら方法を探してみた。

  1. 単純に、別のテーブルを参照する関数をSeries.mapに渡す
    def session_to_timestamp(session_series):
        return session_series.map(lambda x: access_log[access_log.session==x]['timestamp'].iat[0])
    
    result = choice_log.groupby('choice').agg(lambda x: session_to_timestamp(x).min())
    

    Seriesの先頭の要素を取り出す方法には、.iat[0]の他に.iloc[0]や.values[0]などがあり、筆者が試した所values[0]の方が速かったりしたが、pandasのドキュメントに書かれているのはilocとiatなので、ここでは添字が整数なら高速なiatを用いた。

  2. リスト内包表現(list comprehension)で別のテーブルを参照する
    def session_to_timestamp(session_series):
        return [access_log[access_log.session==x]['timestamp'].iat[0] for x in session_series]
    
    result = choice_log.groupby('choice').agg(lambda x: min(session_to_timestamp(x)))
    

    リストにはminメソッドが無いので、session_to_timestamp(x).min()とはできない。

  3. isinを使ったBoolean Indexingで別の表のサブセットを得る
    def session_to_timestamp(session_series):
        return access_log[access_log.session.isin(session_series.values)]['timestamp']
    
    result = choice_log.groupby('choice').agg(lambda x: session_to_timestamp(x).min())
    
  4. 別の表からSeriesを作ってSeries.mapに渡す
    def session_to_timestamp(session_series):
        return session_series.map(access_log.set_index('session').timestamp)
    
    result = choice_log.groupby('choice').agg(lambda x: session_to_timestamp(x).min())
    

    一見シンプルで美しそうだが、set_index()はコピーを返すので、timestampの一時的なdictを作るのと変わらない。しかも、グループ数だけ新たなテーブルを作るので、無駄である。

これらの処理時間を色々測ってみたが、2つのテーブルのサイズやグループの数によって変わり、どう比較すれば良いかわからなかったので、省略する。
大まかな傾向としては、1.と2.の処理時間はchoice_logのサイズに依存し、3.と4.の処理時間はsession_logのサイズに依存するようだった。4.はset_indexした中間テーブルを事前に作っておくと高速化するが、それでも、大抵の場合3.が一番速かった。
いずれの方法も最速になる場合があるようなので、場合毎に色々試してみるしかなさそうである。

肝心のメモリ使用量は、適当な測り方がわからなかった。
そもそも、スワップしながらの処理時間が問題だったので、単純にメモリ使用量では測れないと思う。

他にも、以下のような方向で書き方を考えてみたが、うまくできなかった。

  • groupbyでaggregateでなくtransformしてmin()
    transformする時に別のテーブルを参照することを考えたが、transformするとgroup解除されてしまうので、使えなかった。transformする時にmin()するのなら、min()した値を増殖させるだけ無駄なので、確実にaggregateの方が効率が良い。
  • pandas.DataFrame.lookupを使う
    引数としてindexしか受けられないので、使えなかった。
  • pandas.DataFrame.joinを使う
    pandas.DataFrame.mergeを使うのと変わらなかった。

そもそも、lambda式を使わずに書く方法は無いのだろうか。
teratailとかStack Overflowとかで聞いた方が早いか。

See more ...

Posted at 20:00 in PC一般 | WriteBacks (0)
WriteBacks

Mar 05, 2017

pandas.read_csv+numpy.bincountでエラー

ちょっと前に、はやりのAIのプログラミングでよく使われるPythonとNumPyとPandasを使い始めたのだが、PandasでCSVファイルを読み込んで、各ラベルの出現回数を得る為にNumPyのbincountメソッドを使うと、次のようなエラーが出て、困った。

TypeError: Cannot cast array data from dtype('int64') to dtype('int32') according to the rule 'safe'
その時使った環境は、Windows 7(32bit版)+Python 3.5.2(Anaconda 4.1.1 (32-bit))である。Mac OS Xでは出なかった。

これは、32bitのPythonを使っていると起こることらしい。例えば、32bit Pythonで次のプログラムを実行すると、同じエラーが出る。

import numpy as np
import pandas as pd
import io

data = np.random.randint(10, size=30)
buf = io.StringIO("\n".join(str(x) for x in data))
df = pd.read_csv(buf, header=None)
x = df[0].values
print(x)
print(np.bincount(x))	#Error on 32bit Python
これは、Pandasが32bit Pythonでもint64を使うのが原因のようである。

このエラーを回避するには、bincountに渡すデータの型をint32にすれば良い。

print(np.bincount(x.astype('int32')))	#also OK on 32bit Python
出力例
[1 0 6 7 1 7 7 6 9 5 6 2 8 0 7 6 7 8 9 7 8 9 8 0 2 1 2 6 0 3]
[4 3 3 1 0 1 5 6 4 3]

または、np.unique(return_counts=True)を使う方法があり、こちらの方が、大抵の場合はnp.bincountを使うよりも好ましいとされるようである。

print(np.unique(x, return_counts=True))
出力例
(array([0, 1, 2, 3, 5, 6, 7, 8, 9]), array([4, 3, 3, 1, 1, 5, 6, 4, 3]))
確かに、np.unique(return_counts=True)の方が、データに負の値があっても使えるし、大きな値が混ざってても配列が巨大にならないので、安全そうである。

なお、今動作しているPythonが32bitか64bitかを判定する方法は、いくつかあるようである。 例1

import platform
platform.architecture()
出力(上がMacOSX、下がWin32)
('64bit', '')
('32bit', 'WindowsPE')
例2
import sys
"%x" % sys.maxsize
出力(上がMacOSX、下がWin32)
'7fffffffffffffff'
'7fffffff'
例3(platform.architecture()の実装にも使われている方法)
import struct
struct.calcsize("P") * 8
出力(上がMacOSX、下がWin32)
64
32

See more ...

Posted at 20:47 in PC一般 | WriteBacks (0)
WriteBacks

Mar 01, 2017

Carbon EmacsにPython 3環境導入

筆者はMacで未だにEmacs 22ベースのCarbon Emacsを多用している。Emacs 24ベースのCocoa Emacsもインストールはしているのだが、色々な環境をCarbon Emacsに作ってしまっているので、移行するのが億劫なのである。

最近、Python 3を使い始めたのだが、Carbon Emacsのpython.elはPython 2にしか対応していないので、何とかPython 3に対応させる方法は無いかと探した結果、
http://www.loveshack.ukfsn.org/emacs/
から
python.el
emacs.py
sym-comp.el
の3つをダウンロードして、Emacs.app/内に置けば良いことがわかった。(python.elとemacs.pyは既にあるものを置換、sym-comp.elはpython.elと同じディレクトリに追加)
そして、Emacsを立ち上げて、M-x customize-groupとし、
"Python Default Version" = 3
"Python Python Command" = python3
にすれば完成である。

これで、C-c TABとM-TABを駆使すればシンボルの補完ができて、まあまあコーディングが楽になるのだが、やっぱり補完機能は便利にしたいので、
Pythonの補完をEmacsでシンプルに最小労力で手早く使えるようにする - 牌語備忘録 -pygo
を参考にして、auto-complete.el + ac-python.elをインストールした。
auto-complete.elは、Emacs 22で動作実績のある、auto-complete-1.3.1.tar.bz2をどこかから入手した。

そして、.emacsに次の4行を書けば完成である。

(require 'auto-complete)
(require 'auto-complete-config)
(ac-config-default)
(require 'ac-python)

これで、python-modeにして、import mathと書いてC-c TABして、math.と打つと、math.sqrtやらmath.piやらが補完候補として自動的に現れるようになった。

See more ...

Posted at 23:37 in PC一般 | WriteBacks (0)
WriteBacks