オレオレ文書作成環境のお話

ござ先輩がOmmwriterみたいなIDEあったらいいなぁとかPOSTしてたんで乗っかってみた。

Ommwriter入れてみた

Welcome - OmmwriterからOmmwriterをDLしてインストールしてみた。起動すると表示されるのは雪景色のような壁紙に正方形に囲まれた枠が出てくるだけ。起動中はgrowlも停止するので本当に文章を作成するということに集中することが出来る。僕はデフォルト設定でしばらく作業してみたがどうもタイプしたときの音が気になって切ってしまった。というのも、自分の打鍵音が大きくその後に耳からタイプ音が聞こえてくるとどうも不自然な感じがする。

今の文書作成環境

はてダに書く場合と@ITエンジニアライフのコラムの場合の2通りがある。はてダに書く場合はそのままはてダの編集画面で書いている。いまもまさにその状態。コラムの場合は記事公開する前に自分でもう一度読んだり手直ししたりすることが多いのでEvernoteのエディタで入力し、iPadiPhoneから読み直して手直しなんてことをしている。
Ommwriterはコラム編集用に組み込みたいのだけど残念ながらEvernoteに直接アクセスできる機能を持っていない。そのため、Ommwriterから一度テキストに書き出してコピペで貼付けというフローになりそう。ちょっと手間がかかりそう。この手間とメリットを天秤にかけるとメリットのほうが大きいのでしばらくはこれで試してみようかと思います。

音楽

自然音派の方もいれば、音楽派の方もいるかとおもいます。僕は音楽派なのでiTunesからランダムで再生したり、ずっと同じ曲をリピートで流し続けていたりとその時の気分次第。自宅で作業するときは出来る限りヘッドフォンをしたくないのでスピーカーから音を出してる。最近だとこの辺りのCDがおおい。

AIR オリジナルサウンドトラック

AIR オリジナルサウンドトラック

The world is echoed

The world is echoed

なんでもありだな。おい

椅子とか

テーブルにノートPCをおいて作業しているので、フルバケットシートを座椅子代わりにつかってる。背もたれの部分が頭まであるので座っているとけっこう楽。あとは膝にしょぼーんクッション特大をおいて肘起きにしてちょうどいい高さにしています。写真で撮るとこんな感じ。


人によってはEmacsあれば最強だぜヒャッハーな人も多いかとおもうけど、最近はこのスタイルでおちついてます。

低スペックな頭の僕がQuick JunitとGrowl for windowsを繋いでみた その2

あわせて読みたい
低スペックな頭の僕がQuick JunitとGrowl for windowsを繋いでみた

前回のverではlibgrowl-0.2.0.jarをつかってGrowl for windowsにnotifyを送信してポップアップしていました。ただ、このlibgrowl-0.2.0.jarはGPLなので、拡張とはいえCPLのQuick Junitに勝手に組み込むわけにはいきません。*1この部分を手で実装してしまえば解決できるわけです。

Growl通知の実装はじまります。

ざっくり仕様の解説

Growl for windowsは23053番ポートに常駐して待ち受けをしています。そこにsocketを張ってリクエストを送信します。リクエストはまず、アプリケーションの定義とnotifyの定義を送信します。これをregisterと呼びます。registerが行われるとGrowl for windowsにアプリケーションとnotifyが登録されます。これは呼び出し元アプリケーション単位でおこなうので一度registerを行えばOKです。*2
registerが完了した後にnotifyを行うと画面上にポップアップが表示されます。notifyを行う時はどのアプリケーションのどのnotify定義を利用してポップアップするかというリクエストを送信します。
詳細は次のリンクをごらんください。Growl for Windows

ソース

regtan/quickjunit_growlforwindows - GitHub
新たに実装を行ったものを組み込んだ新しいVerです。buildしてdropinsにでもいれてください。

regtan/growlforwindows4j - GitHub
Growl for windowsJavaから呼び出すライブラリです。

つかうとこんな感じ



まとめ

Javadocも何もほとんどついてないし結構作りが雑な所があるので追々直したい所があったりします。前回と同じ環境でためしてみたところ、おおむね問題なく動作しますが、registerがうまく行かないことがあります。*3Eclipseを再起動するとうまくいくのでGrowl側の仕様かと思います。既知の問題などはREADMEをごらんください。
仕様をみるとリモートのGrowlに対してもnotifyを送信できるのでCIでビルドが終わったらnotifyを送信みたいなこともできるのかな。リモートに対しての処理は実装していないので今後の課題です。

*1:多分ダメだと思う

*2:別に何度やってもいいみたい

*3:registerされたアプリを消した後に再実行するとregisterがうまく行かない

低スペックな頭の僕がQuick JunitとGrowl for windowsを繋いでみた

あわせて読みたい
Quick JUnit Plugin for Eclipse

vimでもなくemacsでもなくEclipseを使ってJava書く人の多くがお世話になっているQuick Junitっていうプラグインがあります。これをいれておくとCtrl+0でテストを実行できたりCtrl+8でテスティングペアに飛んだりとTDDでは色々と便利な機能が拡張されます。4/25にリリースされた新しいVer.0.6.0ではテスト結果をGrowlに通知するExtensionが提供されています。

Growl対応はMacだけです。
Growl対応はMacだけです。

大事なことなので2度言いました。インストールすら出来ません。自宅ではMacユーザな僕ですが会社ではWindowsユーザなのでぐぬぬとなるしかありませんでした。というわけでtwitterでとりあえずつぶやいてみるテストパターンを実行してみました。

Growl for windows対応はじまります。

Growl for windowsに対応させる

Growl自体はmac os向けのアプリですが、Growl for windowsというWindows向けGrowl cloneがあります。軽く検索した所、JavaからGrowl for windowsにnotifyを送信するライブラリ*1があったのでこれを利用してJunitの結果をGrowlに通知するようにしてみました。多くは元々のQuick JunitGrowl Extensionのソースをそのままです。

ソース

regtan/quickjunit_growlforwindows - GitHub
junit.extensions.eclipse.quick.windows.growl.internal.TestListenerForGrowlがテストの結果をGrowlに通知している部分です。その他の解説はREADMEを読んでください。libgrowl-0.2.0.jarがGPLなので念のため外してあります。ビルドする際はDLしてビルドパスに追加してください。

使ってみるとこんな感じ



kompiroさんからのリプライ

メンテナのkompiroさんからリプライを頂きました。

日本語メソッド名については問題なく表示されています。また、連続実行時に通知がまとめられてしまうというのも試してみましたが全てポップアップした所をみると問題ないかと思います。

まとめ

使う時はQuickJunitとGrowl for windowsをインストール後、ソースをビルドして${ECLIPSE_HOME}/dropinsに入れてください。READMEに書いてある位置にアイコン入れるとカスタマイズできるという便利機能*2もついてるので好きなアイコンをいれてご利用ください。

*1:http://sourceforge.net/projects/libgrowl/

*2:どうみても既知の不具合です。ほんとうにありがとうございました。

ほんとうの4月からの新生活と向き合えますか

新卒準備カレンダー2011春 3/27分エントリです。ちなみに昨日は@_mipo_さんでした。

昨日のエントリ:社会人になるということ - mihoのお勉強ブログ(仮)

お前、誰よ?

草食系妙齢プログラマ*1です。大学を新卒して大手SIerに入社しました。でも、1年で退職してお誘いのあった大学院に進学しました。大学院卒業後に別の中小のIT企業に入りました。今の会社で5年目、4月から6年目になります。最近のお仕事はお客さんの社内システムの運用維持*2マイグレーション*3の中でソース書いたり、プロジェクト全体の管理をしたり、たまに工数見積もりなどのお仕事も担当してます。どちらかというとスーツよりな仕事が増えて来たエンタープライズ系のお仕事です。
お仕事以外だとjava-jaというエンジニアのコミュニティによく顔をだしてます。あとはGenesisLightningTalksという勉強会にもよく参加してます。OSSプロジェクトJiemamyのコミッタもやってます。
あと、@ITのエンジニアライフでコラム書いています。不思議そうで不思議でないちょっと不思議な現場の話

お前、何がいいたいの?

大事なこと最初に。
仕事って最終的にはさまざまなコストと向き合うことなんだと思っています。そのコストをさげるために技術力は裏切らない。コミュニケーション力があれば何でもOKの様な風潮もみられますが、それだけでは食ってはいけません。それを身につけるために自分に投資するコストをケチってはいけません。きっとそれはエントロピーを超越して自分に帰ってくるものだとおもいます。

「こんなの絶対おかしいよ」とコスト

単純に顧客とのコミュニケーションと言ってしまうと語弊があるかもしれませんが相手とやり取りをしてソリューション(解決法や改善策)を提供するのもわたし達ITエンジニアの仕事なのです。システムを作るのもわたし達の仕事だし、それを動かすのもわたし達の仕事です。つまり、自分が産んだものを最後まで面倒をみていくわけです。でも、そんな中で「こんな運用と向きあえますか」とか「こんなの絶対おかしいよ」と思えることが少なからずあります。そこで何がおかしいのか、何がダメなのかを相手にアピールすることが必要です。
でも、趣味のプログラミングであれば直してリリースという感じですむのですが、仕事となれば別です。必ず、コストという概念がつきまといます。簡単いうとその現象を解決するのにいくらかかるか、解決したことでお客さんはどれだけ得をするかっていうことです。QBさんのいうところの「願いを叶えてやるから魔法少女になれや(直してやるからコスト払えや)」ってことですね。
仕事を初めてすぐにはわからないかもしれませんが、コストという概念を頭のすみにいれておいてください。自分の考えた解決法でどれだけのバリューを生み出すことがわかってくるとさらに仕事が楽しくなります。

スーツをきたモヒカンを目指すorモヒカン装備のスーツを目指す

数年前、ギークとスーツという話が様々な所でなされました。簡単に言うとスーツはSIerなどシステムの企画やプロジェクトマネージメントしてそろばんをはじく人々、ギークはシステムを設計をしたり、実装をしたりする人です。ギークの中でもプログラミング能力にたけ、日々自分の手斧の手入れを忘れずにヒャッハーと生きている人たちを尊敬と畏怖の思いをこめて「モヒカン族」と呼びます。
今のIT業界にはモヒカン装備のスーツはいません。つまり、スーツな部類の方々は技術に興味をなくしてしまいました。数年来、実装を担当=下流という概念が広まってしまったため、システムがどのように動いているか知らないスーツが大量生産されてしまいました。とあるSIerでは「弊社にはSEしかいません」と社長が大きく表明してしまい業界から苦笑されてしまったという過去があります。一方、スーツを着たモヒカンは多くの方がおられます。アジャイル開発という動くものを基準とした開発手法がひろまり、プロジェクトが少人数のチームによって運営されるようになったため、これまではモヒカンでヒャッハーってやってた人もスーツ的能力を身につける必要が出て来たからです。
「入社してすぐSEになりたい、プログラミングなんてしたくない」って思う方も多いかと思います。わたしも最初の会社に入社した時にそうおもいました。それまでHTMLとかExcelでちょこっと書いたりはできましたがJavaなんて一切触ったことがなかったわけです。べつにプログラミングができなくてもSEにはなれるとおもっていましたが、出向先の会社にいるときの上司に「じゃあどうやってお客さんの悩み事を効率よく解決するの?」と言われたことがあります。つまり、ソリューションを提供するにもそれを実現する手段をしらない奴にSEなんてできないよってことです。
結局の所、両方ある程度身につけないといけないということです。スーツとギークどちらが偉いかということはありません。ただ、お客さんの要件をそのまま伝言ゲームするだけの存在なんて誰も必要としてくれません。きちんと技術的根拠によって解釈された情報を提供できるような人こそが求められています。

たまには息抜きも必要

気が向かない時に仕事しても効率がいいことはありません。そんな時はさっさと帰って、美味しいもの食べて、好きなことしてさっさと寝た方がマシです。残業が多いのって結局はマネージメントミスであって美徳ではありません。自分が宣言した時点での進ちょくが思わしくなかったのならば自分のマネージメントミスか、その個人のスキルにみあった仕事が割り振られていないというマネージメントミスです。間に合わないなら間に合わない宣言しちゃったほうが精神衛生上いいですし、誰にも迷惑をかけません。
さっさと退社して遊びに行くなり、家で好きなことするなりしましょう。関係ないですがまどかマギカの11話からが放送休止になってかなりショックです。

まとめ

すこしとりとめのないかんじになってしまいましたが、現状のわたしのみているIT業界からいえることをまとめてみました。震災のあった影響で順番が少し遅くなりましたが4月から新社会人となる方向けのエントリを集めた新卒準備カレンダー2011春も折り返し地点となりました。まだまだ素敵なエントリがつづきますのでどうぞご参考になさってください。
これをよんでくれた方とどこかでお会いできることを楽しみにしています。

おすすめの本

小悪魔女子大生のサーバエンジニア日記 ――インターネットやサーバのしくみが楽しくわかる

小悪魔女子大生のサーバエンジニア日記 ――インターネットやサーバのしくみが楽しくわかる

小悪魔本はネットワークの基本を学ぶのにちょうどいいか書籍です。ここからステップアップしていきましょう。QBさんの営業力を身につけましょう。
Ringo 1 (ヤングマガジンコミックス)

Ringo 1 (ヤングマガジンコミックス)

ユーザーに愛されるものづくりという観点で読むと非常に面白い漫画です。

明日は

あすは@understeerさんです。

*1:http://www.slideshare.net/regtan/ss-4672891

*2:障害対応やユーザサポートがメインでサーバの管理もします

*3:古いサーバから新しいサーバへのリプレイスに伴うお仕事

低スペックな頭の僕がJavaの機械学習ライブラリmahoutをH2つないでみる。

mahoutのDataModelはJDBCをつかってDBのデータから作ることが出来ます。0.4ではMySQLのみ、0.5-SNAPSHOTではMySQLPostgreSQLのみが標準でサポートされ他のDBを利用する時にはAbstractJDBCDataModelを継承して実装します。

とりあえずH2で

設定も特に必要ないからH2との接続をやってみます。プロジェクト全体はこんな感じ。

pom.xml

mahoutで使うものにH2のJDBCを追加しる

  <dependencies>
  	<dependency>
  		<groupId>org.apache.mahout</groupId>
  		<artifactId>mahout-core</artifactId>
  		<version>0.4</version>
  	</dependency>
  	<dependency>
  		<groupId>com.github.mcpat.slf4j</groupId>
  		<artifactId>slf4cldc-nop</artifactId>
  		<version>1.6.0</version>
  	</dependency>
  	<dependency>
  		<groupId>com.h2database</groupId>
  		<artifactId>h2</artifactId>
  		<version>1.3.153</version>
  	</dependency>
  </dependencies>

テーブル

columName type PK
USER_ID BIGINT PK
ITEM_ID BIGINT PK
PREFERENCE REAL
UPDATING_TIME TIMESTAMP

テーブルのカラム名はDataModelのコンストラクタで指定するので変更可能。でも、Javadoc読むとUPDATING_TIMEの部分は必要ないかもしれない。でもMySQLJDBCDataModelのソース見ると参照するSQLがあるので念のためつけておく。デフォルト値は好きに設定する。

ソース

まずはH2からDataModel作ってくれるH2JDBCDataModel.java

package org.kirino.data.model;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.model.jdbc.AbstractJDBCDataModel;
import org.apache.mahout.common.IOUtils;

public class H2JDBCDataModel extends AbstractJDBCDataModel {

	private static final long serialVersionUID = 1L;
	private final String updatePreferenceSQL;

	public H2JDBCDataModel(DataSource dataSource, String preferenceTable,
			String userIDColumn, String itemIDColumn, String preferenceColumn,
			String timestampColumn) {
		super(dataSource, preferenceTable, userIDColumn, itemIDColumn,
				preferenceColumn,
				// getPreferenceSQL
				"SELECT " + preferenceColumn + " FROM " + preferenceTable
						+ " WHERE " + userIDColumn + "=? AND " + itemIDColumn
						+ "=?",
				// getPreferenceTimeSQL
				"SELECT " + timestampColumn + " FROM " + preferenceTable
						+ " WHERE " + userIDColumn + "=? AND " + itemIDColumn
						+ "=?",
				// getUserSQL
				"SELECT DISTINCT " + userIDColumn + ", " + itemIDColumn + ", "
						+ preferenceColumn + " FROM " + preferenceTable
						+ " WHERE " + userIDColumn + "=? ORDER BY "
						+ itemIDColumn,
				// getAllUsersSQL
				"SELECT DISTINCT " + userIDColumn + ", " + itemIDColumn + ", "
						+ preferenceColumn + " FROM " + preferenceTable
						+ " ORDER BY " + userIDColumn + ", " + itemIDColumn,
				// getNumItemsSQL
				"SELECT COUNT(DISTINCT " + itemIDColumn + ") FROM "
						+ preferenceTable,
				// getNumUsersSQL
				"SELECT COUNT(DISTINCT " + userIDColumn + ") FROM "
						+ preferenceTable,
				// setPreferenceSQL
				"INSERT INTO " + preferenceTable + '(' + userIDColumn + ','
						+ itemIDColumn + ',' + preferenceColumn
						+ ") VALUES (?,?,?)",
				// removePreference SQL
				"DELETE FROM " + preferenceTable + " WHERE " + userIDColumn
						+ "=? AND " + itemIDColumn + "=?",
				// getUsersSQL
				"SELECT DISTINCT " + userIDColumn + " FROM " + preferenceTable
						+ " ORDER BY " + userIDColumn,
				// getItemsSQL
				"SELECT DISTINCT " + itemIDColumn + " FROM " + preferenceTable
						+ " ORDER BY " + itemIDColumn,
				// getPrefsForItemSQL
				"SELECT DISTINCT " + userIDColumn + ", " + itemIDColumn + ", "
						+ preferenceColumn + " FROM " + preferenceTable
						+ " WHERE " + itemIDColumn + "=? ORDER BY "
						+ userIDColumn,
				// getNumPreferenceForItemSQL
				"SELECT COUNT(1) FROM " + preferenceTable + " WHERE "
						+ itemIDColumn + "=?",
				// getNumPreferenceForItemsSQL
				"SELECT COUNT(1) FROM " + preferenceTable + " tp1 JOIN "
						+ preferenceTable + " tp2 WHERE tp1." + itemIDColumn
						+ "=? and tp2." + itemIDColumn + "=?", "SELECT MAX("
						+ preferenceColumn + ") FROM " + preferenceTable,
				"SELECT MIN(" + preferenceColumn + ") FROM " + preferenceTable);
		updatePreferenceSQL = "UPDATE " + preferenceTable + " SET "
				+ preferenceColumn + " = ? WHERE " + userIDColumn + " = ? AND "
				+ itemIDColumn + " = ?";

	}

	@Override
	public void setPreference(long userID, long itemID, float value)
			throws TasteException {

		try {
			super.setPreference(userID, itemID, value);
		} catch (TasteException e) {
			Connection conn = null;
			PreparedStatement stmt = null;
			try {
				conn = super.getDataSource().getConnection();
				stmt = conn.prepareStatement(updatePreferenceSQL);
				stmt.setFloat(1, value);
				stmt.setLong(2, userID);
				stmt.setLong(3, itemID);
				stmt.executeUpdate();

			} catch (SQLException sqle) {
				throw new TasteException(sqle);
			} finally {
				IOUtils.quietClose(null, stmt, conn);
			}
		}

	}

}

実行する部分 MahoutSampleH2.java

package org.kirino.mahout;

import java.util.List;

import javax.naming.NamingException;

import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;
import org.kirino.data.model.H2JDBCDataModel;

public class MahoutSampleH2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			String url = "jdbc:h2:~/hoge";
			String user = "sa";
			String password = "";
			// データの取り込み
			DataModel dataModel = new H2JDBCDataModel(
					H2Datasource.getDatasource(url, user, password),
					"TASETE_PREFERENCES", "USER_ID", "ITEM_ID", "PREFERENCE",
					"UPDATING_TIME");
			// 相関性の評価基準の設定
			UserSimilarity similarity = new PearsonCorrelationSimilarity(
					dataModel);
			// 評価の近い人を探すロジックを決めてる?
			UserNeighborhood neighborhood = new NearestNUserNeighborhood(3,
					similarity, dataModel);
			// レコメンダの作成
			Recommender recommender = new GenericUserBasedRecommender(
					dataModel, neighborhood, similarity);

			// 1番の人に対するレコメンドが1つ
			List<RecommendedItem> recommendations = recommender.recommend(1, 1);
			for (RecommendedItem recommendation : recommendations) {
				System.out.println(recommendation);
			}

			System.out.println("end");

		} catch (TasteException e) {
			e.printStackTrace();
		} catch (NamingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

}

Datasource取ってる部分 H2Datasource.java

package org.kirino;

import javax.naming.NamingException;
import javax.sql.DataSource;

import org.h2.jdbcx.JdbcDataSource;

public class H2Datasource {

	public static DataSource getDatasource(String url,String user,String password) throws NamingException {
		JdbcDataSource dataSource = new JdbcDataSource();
		dataSource.setURL(url);
		dataSource.setUser(user);
		dataSource.setPassword(password);

		return dataSource;
	}

	private H2Datasource() {
		super();
	}

}

解説するよ

まずはH2からDataModel作ってくれるH2JDBCDataModel.javaはコンストラクタとsetPreferenceを実装しています。コンストラクタはそれぞれ該当するSQLをがちゃがちゃと文字列結合してる。MySQLJDBCDataModelもこんな感じの実装。違う点はデータをinsertするSQLMySQLではINSERT〜ON DUPLICATE KEY UPDATE構文でキー重複したらUPDATEということができるけどH2ではできない。だからsetPreferenceをオーバーライドしてる。setPreferenceはAbstractJDBCDataModel#setPreferenceでinsert文を発行して失敗したらH2JDBCDataModel#setPreferenceでupdate文を発行する。この辺がなんかイケてない気がする。
次に実行部分のH2MahoutSample.java csv取り込みと違う点はDataModelの作り方のちがいだけ。

DataModel dataModel = new H2JDBCDataModel(
			H2Datasource.getDatasource(url, user, password),
			"TASETE_PREFERENCES", "USER_ID", "ITEM_ID", "PREFERENCE",
			"UPDATING_TIME");

コンストラクタでDatasourceとテーブル名とそれぞれのカラム名を設定する。この辺はMySQLJDBCDataModelと同じ。カラム名変えたらここを変更するだけでいい。
最後はH2Datasource.java web.xml使わずに書く方法を忘れないようにメモ。

まとめ

今回は0.4を使ってるのでH2JDBCDataModel#setPreferenceの実装がこのような形になっていますが0.5-SNAPSHOTから各SQLのアクセサが入るのでもうちょっと綺麗な処理に書き換えることができます。この部分はDBの実装でキー重複したらUPDATEの処理があるならそちらを使う方が適切でしょう。

Eclipseで大量のクラスから目的のクラスを見つけ出す

今担当しているプロジェクトのJavaファイル数が数万ファイルという結構アレゲな状態になっています。しかも、それを一つのJavaプロジェクトで扱っているので目的のクラスを見つけ出すのも一苦労です。*1デバッグや調査であっちにいったりこっちにいったりしてるうちに見失うことがよくあります。
既に開いているクラスであればCtrl+3*2で一覧表示できるのでこちらから移動できます。

クラス名やEclipseのワークベンチ上の部分の名称(EditorとかViewとか)でフィルタがかかるので楽に切り替えが可能です。でも、これは既に開いているクラスのみが対象です。新たに開く際はOpen Resourceを使います。日本語化してあると「型を開く」とかシャレオツな名前になってます。たしか。ショートカットはCtrl+Shift+R*3

開きたいクラスを正規表現などでフィルタリングすることも出来ます。Openの隣の△をクリックすると指定したエディタで開くことが出来ます。ここまでくれば後はmylynでタスクつくって開いた場所を保存しておけば作業しやすくなります。

@regtan つ Mylyn
Twitter / @やましろ: @regtan つ Mylyn ...

@regtan つ Ctrl+Shift+R
Twitter / @やましろ: @regtan つ Ctrl+Shift+R ...

ってymsr先生に教えてもらいました。

ちなみにEclipseのショートカットですがJavaルールブックの最後に画像付きで解説があります。

Javaルールブック ?読みやすく効率的なコードの原則

Javaルールブック ?読みやすく効率的なコードの原則

今読んでる最中なんですがJavaのお約束事が実にわかりやすい形で書かれているのでプロジェクトみんなの意識統一するのに手元にあると嬉しい本です。近いうちに書評を書く予定です。

*1:パッケージは別れてるのですが最適な分割でない。フレームワーク命名規約上最適分割が出来ない・・・orz

*2:Macではcommand+3

*3:Macではcommand+Shift+R

低スペックな頭の僕がJavaの機械学習ライブラリmahoutを動かしてみる。

あわせて読みたい
Apache Mahoutで機械学習してみるべ - 都元ダイスケ IT-PRESS

地豆んじゃーリーダーが面白そうなライブラリでエントリあげていたので乗っかってみる。基本的に機械学習がなんたるかとか機械学習の結果を統計学的に云々なんてことはよくわかってないのでその辺はリーダーのエントリをお読みください。

Apache mahoutって何よ?

Apache Mahout:: Scalable machine-learning and data-mining library
スケーラブルな機械学習ライブラリって書いてあります。でも、俺たちの目指すスケーラブルってちょっと違うんだぜ!ともいっています。

  • 巨大なデータに対してスケーラブル
    • Hadoop使っても使わなくても大丈夫だよ!
  • 様々なビジネスに対してスケーラブル
    • Apache Software Licenseで提供するよ!
  • スケーラブルコミュニティ
    • 何かあったらコミュニティで議論しようぜ!まってるぜ!

具体的に何が出来るかというと、データの集合体からレコメンドの抽出とかクラスタリング処理をすることが出来ます。

とりあえず動かしてみる

CSVを取り込んでレコメンドを取得してみます。プロジェクト全体像は以下の通りです。

pom.xml

まずは必要なライブラリの設定。mahout-coreとslf4cldc-nopが必要なのでdependenciesは以下の通りに設定。JDKは1.6以降のみ対応です。切り替えをわすれないように。

  <dependencies>
  	<dependency>
  		<groupId>org.apache.mahout</groupId>
  		<artifactId>mahout-core</artifactId>
  		<version>0.4</version>
  	</dependency>
  	<dependency>
  		<groupId>com.github.mcpat.slf4j</groupId>
  		<artifactId>slf4cldc-nop</artifactId>
  		<version>1.6.0</version>
  	</dependency>
  </dependencies>

取り込むcsv

取り込むcsvはこんな感じです。

1,101,1.5
1,102,4.5
1,103,2.5
1,104,2.5
1,105,1.0
2,101,2.0
2,102,3.5
2,103,3.0
2,104,2.5
2,105,3.0
2,106,4.0
3,101,2.5
3,104,3.0
3,105,5.0
3,107,5.0
4,101,5.0
4,103,3.0
4,104,4.5
4,106,4.0
4,107,1.0
5,101,4.0
5,102,3.0
5,103,2.0
5,104,5.0
5,105,3.5
5,106,1.0
6,101,2.0
6,102,4.0
6,103,1.0
6,104,5.0
6,105,3.5
6,106,1.0

順にUser、Item、Preferenceです。それぞれユーザーID、アイテムID、評価点(お気に入り度みたいなかんじ)を示しています。それぞれの型定義はUserとItemはlong。PreferenceはFloatで記述します。*1何かのシステムであればこれが売上データであったり、ユーザレビューの結果であったりといった感じです。
ここで適当にデータを作るとオススメデータが何もないという('・ω・`)ショボーンな結果になります。

ソース

レコメンドしてくれるサンプルソースです。

package org.kirino.mahout;

import java.io.File;
import java.io.IOException;
import java.util.List;

import org.apache.mahout.cf.taste.common.TasteException;
import org.apache.mahout.cf.taste.impl.model.file.FileDataModel;
import org.apache.mahout.cf.taste.impl.neighborhood.NearestNUserNeighborhood;
import org.apache.mahout.cf.taste.impl.recommender.GenericUserBasedRecommender;
import org.apache.mahout.cf.taste.impl.similarity.PearsonCorrelationSimilarity;
import org.apache.mahout.cf.taste.model.DataModel;
import org.apache.mahout.cf.taste.neighborhood.UserNeighborhood;
import org.apache.mahout.cf.taste.recommender.RecommendedItem;
import org.apache.mahout.cf.taste.recommender.Recommender;
import org.apache.mahout.cf.taste.similarity.UserSimilarity;

public class MahoutSample {

	/**
	 * Mahoutの簡単なサンプル 1番のユーザに対するレコメンドを1つだけ出力します。
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			// データの取り込み
			DataModel dataModel = new FileDataModel(new File(
					"src/main/resources/data.csv"));
			// 相関性の評価基準の設定
			UserSimilarity similarity = new PearsonCorrelationSimilarity(
					dataModel);
			// 評価の近い人を探すロジックを決めてる?
			UserNeighborhood neighborhood = new NearestNUserNeighborhood(3,
					similarity, dataModel);
			// レコメンダの作成
			Recommender recommender = new GenericUserBasedRecommender(
					dataModel, neighborhood, similarity);

			// 1番の人に対するレコメンドが1つ
			List<RecommendedItem> recommendations = recommender.recommend(1, 1);
			for (RecommendedItem recommendation : recommendations) {
				System.out.println(recommendation);
			}

			System.out.println("end");

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (TasteException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}

解説するよ!

// データの取り込み
DataModel dataModel = new FileDataModel(new File("src/main/resources/data.csv"));

まずはcsvファイルからDataModelを作成します。今回はファイルからの取り込みですが、MySQLから取り込むものやJDBC経由で汎用的にデータを取り込むクラスがあります。この辺はちょっと調べると色々出来るような感じ。

// 相関性の評価基準の設定
UserSimilarity similarity = new PearsonCorrelationSimilarity(dataModel);

類似性の尺度の設定です。PearsonCorrelationSimilarityはアイテム毎のお気に入り度の平均を求めるときに相乗平均を利用するようです。*2他にSpearmanCorrelationSimilarityやTanimotoCoefficientSimilarityなどがあります。それぞれ計算式がことなるようです。

// 評価の近い人を探すロジックを決めてる?
UserNeighborhood neighborhood = new NearestNUserNeighborhood(3,similarity, dataModel);

正直この辺からよくわかってません。アイテムとその評価点の傾向をから似たユーザを探す処理のようです。第一引数が一人のユーザに対して何人の傾向の似ているユーザを探すか指定しています。これくらいのデータだと1に設定すると結果が出ないかも。

// レコメンダの作成
Recommender recommender = new GenericUserBasedRecommender(dataModel, neighborhood, similarity);

データと評価の近い人のデータからレコメンダを作成します。他のレコメンダもあります。

// 1番の人に対するレコメンドが1つ
List<RecommendedItem> recommendations = recommender.recommend(1, 1);

レコメンダからレコメンデーションを取り出します。今回はユーザIDが1に対するレコメンデーションを1つ取り出します。データが多ければきっと2個でも3個でも出るかと思います。今回は1個しかでませんでした。

結果

RecommendedItem[item:106, value:3.9137328]
end

1番の人に対するオススメはアイテム106で評価点は3.9くらいつけるんじゃねーの?とmahoutくんはいってます。オススメできない場合はなにも出力されません。あと評価点が計算できない場合はNaNを返却するらしいです。

まとめ

amazonのオススメとかこういう感じで裏で動いてるみたいです。mahout使ってるかどうかは知りませんが。サイトでJavadocがみれないのはちょっと痛い*3ですけどなかなかおもしろそうです。今回はcsvから取り込みましたがDBからも直接取り込めそうなのでいろいろ出来そうな感じです。はい。

*1:JavaDocをみるとそれぞれの返すメソッドがその型になってる

*2:JavaDocによると

*3:mahoutのサイトでは404になってる。CIもみれない。javadocだけならこちら