23 Dec 2017, 10:12

データサイエンティスト養成講座を受けた感想(後半)

はじめに

データサイエンティスト協会が主催するデータサイエンテスィト養成講座を受けた。

前半の感想は以下です。今回は、後半の感想を書く。

各回の内容

第5回

中間発表会。銀行問題の成果発表。私の成績は、17位でした。各班の発表のあとは、先生のコメント。この銀行問題は、それほどデータ前処理をしなくても、random forestや xgboostのちからを借りると、そこそこのスコアがでてしまうとのこと。

ただし、それはコンペという特殊な世界で通用することであり、ビジネス的観点が抜けていることはよくない。業務でつかう多くのモデルは説明が求められる。

そして、後半のお題、Jリーグの観客動員数を予測せよ! が出される。

こちらは、単純にxgboostやrandom forest を 適用したたけでは、いい成果はでない。しっかり特徴量エンジニアリングをする必要がある。

第6、7回

後半は、目新しい知識の講義はなく、グループワーク中心で進む。チームでアイデアを出しながら特徴量を作成していく。

モデルの改善方法として、残差を見ながら分析する方法が紹介される。どの場合に、モデルの予測が結果を外しているかを並べて、傾向を見て、それをモデルに反映するというサイクルが紹介される。

チームでサッカーに詳しい人が一人いて、(ほかはそれほど詳しくなくて)、アイデアをもらいながら特徴量を作っていった。また、自分では、コーディング力がなくて作れない特徴量も他のメンバの手を借りて、作った。一人で考えているよりも、みんなで考えた方がよいアイデアが浮かぶ。協力が必要。

私ともう一人はpythonでデータ分析をして、他のメンバはRでデータ分析を進めた。あるメンバがRの分析力をメキメキつけて、驚いた。

という本を読んで力をつけたそうだ(これはウェブで無料公開されている)。python と Rで、どちらがよいかという議論をよく見かけるが、dplyr という強力な前処理ツールがあるので、モデルの前処理は R でやるほうがよいと感じた。前処理や視覚化は、pythonよりもRに軍配が上がる。

第8回

最終発表会。

私の成績は、なんと1位だ!

ネタバレになるので、作った特徴量は秘密だが、思いついた特徴量はすべて試した。

そして、最終秘密兵器、catboostを使った。

これは、ロシアのGoogleと言われているYandexが開発したOSS。これをつかったら、スコアがグーンと下がった。私は、データの視覚化をあまりしなかったのだけれども、同じ班のさきほどR力をグーンと伸ばした人がいろいろとデータを視覚化してくれた。

感想

データサイエンティスト養成講座にかけた勉強時間は100時間くらい。

当初の目的だったCSV形式のデータに対する解析技術がついた(Python力も)。

反省点としては、精度を求めるあまり、データの意味を考えることを怠った。データをプロットして、観察して、加工するというプロセスが踏めていなかった。なので、今後はデータを観察し表現する力、人に説明する力を身に着けたい。

これから

これからどうするかということも考えておこう。自分は他のひとと違って、業務ではデータ分析をしていない。なので、ここで学んだことを忘れないようにするためには、継続してデータ分析コンペに参加しようと思う。

DeepAnalyticsや Kaggleのコンペに参加して、腕を磨いていきたい。コンペでいい成績を取ることによって、力をつけていきたい。

今は、データサイエンスに興味があるが、転職をするかどうかは、まだもう少し見極めが必要。もう少し勉強してみて、この分野が本当に面白いと感じたら、転職を考えよう。

25 Nov 2017, 11:11

Logbackのログを見やすくする方法(ファイルをgrep, Lilith)

仕事で、Javaのロギングツールとして slf4j + logbackを使っている。

このログなのだが、IntelliJの出力画面から見ると、みにくい。

できれば、一連のログをgrepしたいのだが、それがIntellJではできないのだ。

そこで、出力されたログをフィルタする方法を調べたところ、2つ方法が見つかった。

ファイルに書き出してgrepする

まずは、簡単な方法から。ログをファイル出力して出力結果をキーワードでfilterする。

filterの方法は、さくらエディタだったり、秀丸エディタだったりの機能を使えば良い。

では、ファイルにログを出力する方法だけれども、以下をlogback.xmlに追加。

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>logs/app.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>logs/app.%d{yyyy-MM-dd}.log.tar.gz</fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="FILE"/>
    </root>
</configuration>

これで logs/app.logにログが出力される。

ログイベントビューアの Lilithをつかう

Lilithは OSSのlogback用ログビューア。

Lilithのために、以下のツールをインストールする。build.gradeのdependenciesに以下を追加。

<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
    <appender name="LogbackClassic" class="ch.qos.logback.classic.net.SocketAppender">
        <RemoteHost>localhost</RemoteHost>
        <Port>4560</Port>
        <ReconnectionDelay>170</ReconnectionDelay>
        <IncludeCallerData>true</IncludeCallerData>
    </appender>

    <appender name="FILE2" class="ch.qos.logback.core.FileAppender">
        <file>logs/classic.lilith</file>
        <encoder class="de.huxhorn.lilith.logback.encoder.ClassicLilithEncoder">
            <IncludeCallerData>true</IncludeCallerData>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="LogbackClassic"/>
        <appender-ref ref="FILE2"/>
    </root>
</configuration>

Lilithを立ち上げておいて、アプリを実行すると、Lilithにログが表示される。

19 Nov 2017, 08:04

データサイエンティスト養成講座を受けた感想(前半)

はじめに

データサイエンティスト協会が主催するデータサイエンティスト養成講座を受けている。

受講の動機

受講の動機は、データサイエンスに興味があり、勉強したかったから。

今まで、機械学習の勉強をしようとして、流行りのDeepLearningにばかり取り組んできた。

しかし、DeepLearningを学んでも現実の問題に適用するのは難しい。

急にAIが流行りだしたから、みんなDeepLearningを勉強し始めたが、

なんか違うというか、ズレている気がしている。

今はお祭り騒ぎのような気がしてならない。

なので、きちんとデータに向き合う力、

そしてそれを現実に応用できる力をつけるためにこの講座に申し込んだ。

裏の理由は、kaggleや DeepAnalyticsで、データがcsv形式で与えられた時、

分類問題や予測問題に取り組む力が欲しかったから。

こういう機械学習コンペの攻略法的な書籍やwebページが探しても見つからないのだ。

この講座では、DeepAnalyticsの練習問題に実際に取り組んでいくことで、

機械学習のモデル構築の手法を学ぶという実践的な講座。

受講生の人たちは、会社から派遣されてきたデータサイエンス部所属の人たちがほとんどだ。

自分は、趣味で自費(8万円!)で、しかも会社には受講していることを秘密にして受講している。

おそらくそんな人はほとんどいない。

概要

DeepAnalyticsのコンペにチームで取り組んでいく。

講座では、まずは講義がありそのあとに演習課題がだされてそれに取り組む。

演習課題はチーム内で相談しながら進めていくので、わからなくても教えあえる。

演習課題は、時間内に終わらないので、それが宿題になる。

チーム内や講義内容の情報共有は、グループウェアのサイボウズLiveが使われる。

各チームごとにグループが作成されて、情報共有できる。

各回の内容

第1回

初回の講座では、Rの基礎とモデルの評価方法が紹介される。

私は今までPythonを使ってきたので、Rのことがわからなかったので、

なれるまでは一苦労だった。

データの処理方法として、dplyrというライブラリが紹介される。

ちょうど、Javaでstreamについて勉強していたので、

dplyrはそれと比較すれば理解はしやすかった。でも、使いこなせる気がしない。。。

モデルの評価方法としてRMSEとAUCが紹介される。これらの指標は、

これからDeepAnalyticsで評価で使われるからだ。

RMSEは理解が容易だが、AUCはちょっと難しい。講座でも、AUCは時間をかけて説明された。

そのあとは、実際にDeepAnalyticsの銀行問題のデータをダウンロードして解析するという

課題が出された。そこまで難しくはない。後日、模範解答が配られ、dplyrをバリバリに

使ったかっこいい解法が紹介されていてちょっとびっくりした。

課題は、はじめは個人で考えるのだが、わからないときはチーム内で相談することができる。

なので、仲良くなる意味でも相談し合った。

第2回

機械学習アルゴリズムとして決定木が紹介される。

Rで決定木をつかうには、rpartというライブラリを使う。

決定木は、ライブラリをつかうとぱっと計算できてしまう。楽だ。

また、前回紹介されたAUCもrRocというライブラリを使うとすぐに計算できる。

そして、決定木でDeepAnalyticsの銀行問題をとりあえず解いて、

適当にパラメータチューニングしてコンテストに提出することをやった。

手元で計算したAUCよりも低い値がでる。なぜでしょう?という流れから、

過学習の説明が始まる。

過学習を防ぐために、ホールドアウト法とクロスバリデーション法が紹介される。

そのあと、パラメータチューニングの方法としてグリッドサーチが紹介される。

このあたりになると自力でのコーディングはちょっとむずかしい。

あたえられたサンプルコードを弄り回してグリッドサーチをする。

家に帰って、課題をPythonで解いてみた。scikit-learnの勉強にもなる。

しかし、どうもpythonで書いた決定木とRで書いた決定木の結果が一致しない。

なので、Rで書いていくことにした。

caretというライブラリを使うと、グリッドサーチが簡単にできるので試してみた。

しかし、caretは rpartモデルではcp, rpart2モデルでは maxdepthしかできない。

やりたいことは複数のパラメータを同時に探索することだ。

いろいろ調べたらmlrというパッケージでそれが実現できることがわかった。

mlr で グリッドサーチをして、目標だった AUC 0.9 以上を達成!

第3回

今回は、リニア系の手法ということで、ロジスティック回帰が紹介される。

kaggleのアンケート調査では、データサイエンティストが一番利用しているのは、

このロジスティック回帰だそうだ。二番目は、決定木。

その理由は、どちらも結果がわかりやすく、説明もしやすいからだそうだ。

そのままでもそこそこの精度はでるのだけれども、より精度を出すために、

データの加工方法が紹介される。

たとえば、絶対値をとったり、logをかましてデータを直線に近くなるようにする。

また、外れ値を削除した。決定木は条件を分岐していくので、外れ値はそれほど影響はないが、

このロジスティック回帰には影響がかなりある。

データをプロットして性質をつかみ、データを加工して、精度を見ていく。

だんだんデータサイエンティストっぽくなってきた。

最後におまけとして、Lasso回帰やRigde回帰も紹介される。

宿題は、ロジスティック回帰を利用して再度投稿してみること。

私は毎度のことながら、Pythonに課題を移植して取り組んだ。

データ加工は外れ値の除去をしてみたら精度が上がった。

が、他のアイデアはでなかった。しかし、そこはチームで取り組んでいるので、

他のメンバがアイデアを提案してくれた。

私は、事前に Pythonではじめる機械学習を読んでいたので、

そこにかかれていたLassoやRidge, Polynomial Featureを使って、精度を上げたがそれは注意された。

ツールを使えば簡単にいい精度がでるけれども、

それだとブラックボックスの機械を使っているだけになってしまいます。

この講座では、データの様子を良くみて、どう工夫するのかという基礎を講習しているつもりです。

なので、結果としてのスコアも大切ですが、何をしているのかを理解することはもっと重要です。

先生がたの熱意や優しさを感じたのだった。

第4回

集団学習(アンサンブル学習)について学ぶ。バギング、スタッキングが紹介される。

まずは、出力結果の平均をとる平均法が紹介される。

単純に今までの決定木系とリニア系の手法を足すだけで、精度が上がった。これは不思議。

次にバギングが紹介される。これも数式はないものの、

わかりやすい図とサンプルコードとともに紹介されるので、理解できた。

次にスタッキング。これは、理解が難しかった。

しかし、このスタッキングを紹介している書籍やウェブページはあまりないため、

この講座の資料はとても貴重なものだと言われる。たしかに、調べても検索で引っかからない。

こちらもわかりやすい図とサンプルコードが与えられるのだが、

会場にいる人のほとんどが理解できずポカーンとしてしまった。

私は、それから家に帰って4時間くらいうんうんうなってようやく理解できた。

課題は、アンサンブル学習を利用してさらなる精度向上を目指すこと。

最後にxgboostとランダムフォレストという究極秘密兵器が紹介され、頑張ってと言われる。

家に帰り、バギングとスタッキングをPythonで実装しなおした。

スタッキングがどうもバグって時間がかかったものの、

どちらもスクラッチでコーディングすることができた。

しかし、そうやって頑張って理解したものよりも、

xgboostとランダムフォレストをつかったほうが簡単にいい精度がでてしったのだった。

これはすごい。さすがはコンペで大活躍している手法のことだけある。

xgboostはパラメータチューニングが難しい。解説サイトがあったので、それを元にチューニング。

xgboostは GPUで走らせることができるので、Amazonの AWSの EC2インスタンスを借りて、

走らせた。自分のNotePCで走らせるのと比べて爆速でびっくり仰天。

最終的には、以下の結果となった。

`○投稿結果: 0.94031

○手法の工夫

○アンサンブルの工夫

  • ランダムフォレストとxgboostの結果を59:41の割合で足した。

次回は、中間発表と、後半の課題Jリーグ問題だ。後半へ続く。

18 Nov 2017, 13:53

JUnitとdbUnitを使ってPostgresのDBテストを実施する

はじめに

データベースの知識がなくって、DBのテスト方法がわからない。

そこで、データベースのテストを自動化するためのツール、dbUnitを調べてみた。

dbUnitをつかうと以下のようなことができる。

  1. JUnitのテスト実施前に、データをデータベースに投入。
  2. テスト実施後に、期待のデータと変更後のデータベースの値を比較。

データはxmlデータ, Excelデータ, csvデータが利用できる。

インストール

ビルドツールにgradleを利用しているので、gradleの設定を書く。

以下から、最新のバージョンをクリックして、gradleのコードをdependenciesに書く。

dependencies {   
    compile group: 'org.dbunit', name: 'dbunit', version: '2.5.4'
}

データのテーブル用意

データのテーブルを用意する。ここでは、flywayを使った。

buildscript {
    dependencies {
        classpath 'com.h2database:h2:1.4.191'
    }
}

plugins {
    id "org.flywaydb.flyway" version "4.2.0"
}

flyway {
    url = 'jdbc:postgresql://localhost:5434/sampleDB'
    user = 'postgres'
    password = 'postgres'
}

本題ではないので、ここでは参考リンクを貼って詳細はそちらに譲る。

以下のようなテーブルをflywayMigrateで作成。V1__create_table.sql

CREATE TABLE HOGE (
    ID INT,
    NAME VARCHAR(255),
    STATE VARCHAR(10)
);

投入データと期待データの準備

データはxml形式のものを用意する。テスト前、テスト後のデータを用意。ID=3の stateを normal から errorに書き換える。

Before.xml

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <HOGE ID="1" STATE="normal" NAME="name#1"/>
    <HOGE ID="2" STATE="normal" NAME="name#2"/>
    <HOGE ID="3" STATE="normal" NAME="name#3"/>
    <HOGE ID="4" STATE="normal" NAME="name#4"/>
    <HOGE ID="5" STATE="normal" NAME="name#5"/>
</dataset>

After.xml

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <HOGE ID="1" STATE="normal" NAME="name#1"/>
    <HOGE ID="2" STATE="normal" NAME="name#2"/>
    <HOGE ID="3" STATE="error" NAME="name#3"/>
    <HOGE ID="4" STATE="normal" NAME="name#4"/>
    <HOGE ID="5" STATE="normal" NAME="name#5"/>
</dataset>

テスト対象コード

テスト対象コードをいかに示す。やっていることは、SQL文を実行しているだけ。

package sample;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class DbUnitSample {
    private static final String SQL = "update HOGE set state = 'error' where name = 'name#3';";

    public void execute() {
        try(Connection conn = DriverManager.getConnection(
                "jdbc:postgresql://localhost:5434/sampleDB", "postgres", "postgres");
        ) {
            PreparedStatement stmt = conn.prepareStatement(SQL);
            stmt.executeUpdate();
        } catch(Exception e) {
            System.out.println("failed.");
        }
    }
}

テストコード

テストコードをいかに示す。

package sample;

import org.dbunit.Assertion;
import org.dbunit.IDatabaseTester;
import org.dbunit.JdbcDatabaseTester;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.SortedTable;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;
import org.dbunit.operation.DatabaseOperation;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.File;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

public class DbUnitSampleTest {
    private static IDatabaseTester databaseTester;
    private DbUnitSample sample;

    @Before
    public void setUp() throws Exception {
        databaseTester = new JdbcDatabaseTester("org.postgresql.Driver",
                "jdbc:postgresql://localhost:5434/sampleDB",
                "postgres",
                "postgres",
                "public");

        IDataSet dataSet = new FlatXmlDataSetBuilder().build(
                new File("src/test/resources/db/dbunit/Before.xml"));
        databaseTester.setDataSet(dataSet);
        databaseTester.setSetUpOperation(DatabaseOperation.CLEAN_INSERT);
        databaseTester.onSetup();

        sample = new DbUnitSample();
    }

    @After
    public void tearDown() throws Exception {
        databaseTester.setTearDownOperation(DatabaseOperation.NONE);
        databaseTester.onTearDown();
    }

    @Test
    public void state更新() throws Exception {
        sample.execute();
        IDataSet expectedDataSet = new FlatXmlDataSetBuilder().build(
                new File("src/test/resources/db/dbunit/After.xml"));
        ITable expectedTable = expectedDataSet.getTable("HOGE");

        IDataSet databaseDataSet = databaseTester.getConnection().createDataSet();
        ITable actualTable = databaseDataSet.getTable("HOGE");
        ITable sortedTable = new SortedTable(actualTable, new String[]{"id"});

        Assertion.assertEquals(expectedTable, sortedTable);
    }

    @Test
    public void state更新2() throws Exception {
        sample.execute();
        IDataSet databaseDataSet = databaseTester.getConnection().createDataSet();
        ITable actualTable = databaseDataSet.getTable("HOGE");

        String actualState = "normal";
        for(int i=0; i < actualTable.getRowCount(); i++) {
            if (actualTable.getValue(i, "name").toString().equals("name#3")) {
                actualState = actualTable.getValue(i, "state").toString();
                break;
            }
        }
        assertThat("error", is(actualState));
    }
}

ちょっと注意するところは、sqlのupdateを実行すると、updateしたid=3が、rowの一番最後になってしまうところ。なので、テスト1では、わざわざソートして対応、テスト2では、for文で名前をキーにして検索をしている。

        ITable sortedTable = new SortedTable(actualTable, new String[]{"id"});

おわりに

手動でデータベースを書き換えてテストするのではなくて、flywayやらdbunitやら、使えるものは使って効率よく開発すべき。

しかし問題は、テストを書く時間すら今は確保できないくらい追い詰められていること。

進捗ヤバメです。(´・ω・`)

code:

18 Nov 2017, 08:27

PowerMockを使ってJavaコードをテストしてみた

はじめに

仕事で JavaのTesting Frameworkとして Spockを利用する予定があったので、一生懸命勉強していたが、政治的な理由によって Spockではなく JUnitが採用されてしまった!

というわけで、急遽 JUnitで利用できそうな Mockフレームワークを調査。

PowerMockというのがなかなかいけているので、それを調べた。

今回のテスト対象コードは、以前書いたSpock Frameworkのときのと同じです。

package sample;

public class MockSample {
    private MessageManager mgr;

    public void setMgr(MessageManager mgr) {
        this.mgr = mgr;
    }

    public MockSample() {
        mgr = new MessageManagerImpl();
    }

    public void sendMsg(String msg) {
        mgr.send(msg);
    }

    public int sendMsg2(String msg) {
        return mgr.send2(msg);
    }

    public void sendMsg3(String msg) {
        MessageManager mgr2 = new MessageManagerImpl();
       mgr2.send(msg);
    }

    public static int sendMsg4(String msg) {
        return MessageManagerStatic.send(msg);
    }

    private void sendMsg5(String msg) {
        mgr.send(msg);
    }
}
package sample;

public interface MessageManager {
    void send(String msg);
    int send2(String msg);
}
package sample;

public class MessageManagerImpl implements MessageManager {
    @Override
    public void send(String msg) {
        System.out.println(msg);
    }

    @Override
    public int send2(String msg) {
        System.out.println(msg);
        return 0;
    }
}
package sample;

public class MessageManagerStatic {
    public static int send(String msg) {
        System.out.println(msg);
        return 0;
    }
}

PowerMock Install w/ gradle

Gradleをビルドツールで利用しているので、gradleの方法を書く。

公式wikiには、Mavenのやり方が書いてあるので、そちらを参照。

build.gradleの dependenciesに以下を追加する。

もっと最新バージョンが出ているらしいが不安定だとwikiに書いてあったので、安定バージョンを。

dependencies {
    testCompile 'junit:junit:4.12'
    testCompile 'org.powermock:powermock-module-junit4:1.6.2'
    testCompile 'org.powermock:powermock-api-mockito:1.6.2'
}

テスト!テスト!テスト!

テストする内容は、比較のためSpock Frameworkと同じにしてます。

まずは、お決まりのおまじないアノテーション2つを書く

  • @RunWith(PowerMockRunner.class)
  • @PrepareForTest({MockSample.class, MessageManagerStatic.class})

    • MockSample.class … メソッド内のインスタンスモック用
    • MessageManagerStatic … メソッド内のスタティックメソッドモック用

      呼び出し引数と回数をチェック

  • times で 呼び出し回数をチェック

  • Mockito.veirfy(spy).send(“Hello”) で引数にHelloが来たかチェック

@Test
public void 呼び出し引数をチェック() {
    MessageManager spy = PowerMockito.spy(new MessageManagerImpl());

    sample.setMgr(spy);
    sample.sendMsg("Hello");
    sample.sendMsg("Hello");

    Mockito.verify(spy, times(2)).send("Hello");
}

戻り値を返す

  • when(spy.send2(“Hello”)).thenReturn(1) で 1を返す.
@Test
public void 戻り値を返す() {
    MessageManager spy = PowerMockito.spy(new MessageManagerImpl());
    when(spy.send2("Hello")).thenReturn(1);
    sample.setMgr(spy);

    int ret = sample.sendMsg2("Hello");

    Mockito.verify(spy, times(1)).send2("Hello");
    assertThat(1, is(ret));
}

メソッド内で生成されるインスタンスをモックに置き換える

  • PowerMockit.whenNiewでMessageManagerImplがnew されたときに、作成したmockオブジェクトにすり替える。

この機能は、いろんなフレームワークを試しきたが、PowerMock/Mockitoで初めて見た。

@Test
public void メソッド内で生成されるインスタンスをモックに置き換える() throws Exception {
    // https://github.com/powermock/powermock/wiki/MockConstructor
    MessageManagerImpl mock = PowerMockito.mock(MessageManagerImpl.class);
    PowerMockito.whenNew(MessageManagerImpl.class).withNoArguments().thenReturn(mock);

    sample.sendMsg3("Hello");

    Mockito.verify(mock).send("Hello");
}

staticなメソッドをテストする

オブジェクト内のメソッドからstaticメソッドを呼んでいたって、モックを利用することで、戻り値を書き換えることができる。

@Test
public void staticなメソッドをテストする() {
    // https://github.com/powermock/powermock/wiki/Mockito#mocking-static-method
    PowerMockito.mockStatic(MessageManagerStatic.class);
    Mockito.when(MessageManagerStatic.send("Hello")).thenReturn(1);

    int ret = sample.sendMsg4("Hello");

    assertThat(1, is(ret));
}

例外を発生させる

モックのメソッドが呼ばれたら例外を発生させることもできる。 PowerMockito.doThrow(new Exception).when(mock).send(“Hello”)で実現している。

@Test
public void 例外が発生しないことを確認する() {
    try {
        sample.sendMsg("Hello");
    } catch (Exception e) {
        fail(e.getMessage());
    }
}

@Test(expected = IllegalStateException.class)
public void 例外が発生したこと確認する() throws Exception {
    MessageManagerImpl mock = PowerMockito.mock(MessageManagerImpl.class);
    PowerMockito.doThrow(new IllegalStateException()).when(mock).send("Hello");
    PowerMockito.whenNew(MessageManagerImpl.class).withNoArguments().thenReturn(mock);

    sample.sendMsg3("Hello");
}

おわりに

PowerMock, メソッド内で生成されるインスタンスをモックに置き換える機能がとても強力。

しかし、最大の問題点は、ドキュメントが少ないことだ。いろいろネットで情報を漁ってみたものの、情報量が少ない。

やっぱりSpockを使いたいなあ。

今回のコードはgithubにもあげています。

08 Oct 2017, 03:25

mlrを使って決定木(rpart)の複数パラメータのグリッドサーチをする

はじめに

Rでグリッドサーチをするためのパッケージとしては、caretが有名なのだが、

決定木でグリッドサーチをしようとすると、一つのパラメータについてしかできなかった。

  • rpartモデルではcpのみ
  • rpart2モデルではmaxdepthのみ

やりたいことは、cp, maxdepth, minbucketパラメータを同時にグリッドサーチをしたい。

自力でやろうとすると、for文が3つの入れ子構造になって大変だ。

いろいろ調査したら、mlr(Machine Larning for R)というパッケージで

複数パラメータに対して、グリッドサーチがかけられるとわかったので、そのメモを残す。

今回のJupyter Notebookは gistにあげています。以下抜粋。

mlrでグリッドサーチ

今回の題材は、毎度おなじみの kaggleタイタニック問題を利用する。

前処理を自分でやるのがだるかったので、以下の記事の内容を利用することにした。

以下の記事を参考にして、タイタニック問題のためのコードを書いた。

以下の範囲を指定してグリッドサーチ。

gs <- makeParamSet(
    makeDiscreteParam("cp", values = c(1e-4, 1e-5)),
    makeDiscreteParam("maxdepth", values = seq(5,10,1)),
    makeDiscreteParam("minbucket", values = seq(10,100,10))
)

すると、指定した範囲の組み合わせを総当りに計算していく。

最終的に、best scoreとなった値が出力されて終了。

それを提出したら、titanicのランキングでスコアが上がった。やった。

01 Oct 2017, 08:43

Java開発でJenkinsを個人運用するためのメモ

はじめに

仕事でJava開発をすることになったのだけれども、

コードの品質をGUIで可視化すると便利だなと思って、

Jenkinsをつかってみることにしました。

あくまで可視化するためのグラフィカルな便利cronとしての役割のみ求めているので、複雑なことや難しいことはしません。

やりたいこと

  • gradle 連携
  • FindBugsの指摘を可視化
  • CheckStyleの指摘を可視化
  • ステップ数を数える
  • TODO 管理
  • spock テスト実行

version

  • Jenkins 2.73.1
  • StepCounter Plugin 2.0.0
  • FindBugs Plugin 4.71
  • CheckStyle Plug-in 3.49
  • Task Scanner Plug-in 4.52

Jenkinsのインストール

お試しなので、自PCのUbuntu 16.04 LTSにインストールします。手順は以下。

仕事では、CentOS 7.4にインストールします。

ポート番号の変更をします。デフォルトでは8080です。

/etc/default/jenkinsを編集して、12345とか適当なものに変更します。

- HTTP_PORT=8080
+ HTTP_PORT=12345

Jenkinsを再起動。

$ sudo systemctl daemon-reload
$ sudo service jenkins restart

http://localhost:12345 にアクセスして、画面が表示されることを確認。

手順にしたがって、初期設定をする。オススメプラグインをインストールする。

リポジトリをチェックアウトしてビルドしてみる

とりあえず以下のgithub repoを利用します。ビルドツールはgradleです。

設定手順

  • General -> プロジェクト名に “hello_jenkins”を入力
  • ソースコード管理 -> git にチェックして、リポジトリURLに https://github.com/tsu-nera/java_spock_playground.git を入力
  • ビルド -> ビルド手順の追加 -> Invoke gradle script を選択

手動でビルドを実行すると、githubからcloneして、gradleコマンドが叩かれる。

プラグインでやりたいことを実現

ステップ数を数える

以下のプラグインを追加。

  • StepCounter Plugin
  • 設定 -> ビルド後の処理 -> ビルド後の処理の追加を選択
  • Step Counter を選択
  • ファイルの種類: java,
  • 解析するファイルパターン **/*.java を入力

FindBugsの設定

以下のプラグインを追加。

  • FindBugs Plug-in
  • 設定 -> ビルド後の処理 -> ビルド後の処理の追加を選択
  • Findbugs警告の集計を選択
  • build/reports/findbugs/main.xmlを入力。
  • build.gradleに以下を追加
apply plugin: 'findbugs'

findbugs {
    reportsDir = file("./build/reports/findbugs")
    ignoreFailures = true
}

CheckStyleの設定

  • CheckStyle Plug-in
  • 設定 -> ビルド後の処理 -> ビルド後の処理の追加を選択
  • CheckStyle警告の集計を選択
  • build/reports/checkstyle/main.xmlを入力。
  • build.gradleに以下を追加
apply plugin: 'checkstyle'

checkstyle {
    reportsDir = file('./build/reports/checkstyle')
    configFile = file('./sun_checks.xml')
    ignoreFailures = true
}
  • ここから、checkstyleの設定ファイルを取得してプロジェクトルートに置く。 https://github.com/checkstyle/checkstyle/tree/master/src/main/resources
    • google_checks.xml
    • sun_checks.xml
  • 設定-> ビルド -> ビルド手順の追加 -> Invoke gradle script を選択
  • Use Gradle Wrapperを選択。Tasksのところに以下を入力。

    checkstyleMain findbugsMain

TODO を表示

  • TaskScanner Plug-in
  • 設定 -> ビルド後の処理 -> ビルド後の処理の追加を選択
  • 未解決タスクの集計を選択
  • 集計対象に **/*.javaを入力

spock テスト実行

  • 設定 -> ビルド後の処理 -> ビルド後の処理の追加を選択
  • JUnitテスト結果の集計を選択
  • テスト結果xmlに **/*Spec.xmlを入力
  • 設定-> ビルド -> ビルド手順の追加 -> Invoke gradle script を選択
  • Use Gradle Wrapperを選択。Tasksのところに以下を入力。

    clean test

ジョブの実行

エラーするので以下をプロジェクトルートで叩いた。

sudo gradle wrap

01 Oct 2017, 06:13

Java開発でIntelliJ IDEAにとりあえず入れたプラグインたちのメモ

はじめに

仕事でIntelliJ IDEAを使って Java開発することになったので、

とりあえず便利そうなプラグインをみつくろって入れてみたので、そのメモ。

IntelliJはデフォルトでいろんなプラグインがすでにインストールされているのが嬉しい。

プラグイン

静的解析

  • FindBugs-IDEA
  • CheckStyle-IDEA

FindBugsと CheckStyleは定番な静的解析ツールなので、とりあえず入れておく。

ちなみに、InteliJでは、Ctrl + Shift + Alt + L で コードのフォーマット整形をしてくれる。

テスト

Spock Frameworkを使うので、サポートツールを入れる。ハイライトするだけかな?

  • Spock Framework Enhancements

ステップカウント

進捗はステップ数で報告することになっているので、

ステップカウンタ(LOC)がIDEから使えると便利。

  • Statistics

日毎のステップ数が差分でわかればいいのだが、これはエクスポート機能がみつからない。

ステップ数については、別のツールを使ったほうがいいかな。Jenkinsをかませるか?

保管アクション

保管をトリガにして、いろいろな機能を動かす。

  • Save Action

設定 -> その他の設定 -> 保管アクションから各種設定を有効にする。

これはある意味、チーム開発しているときは、

勝手にいろいろな部分を修正してしまう恐れがあって危険だな。

Lombok

  • Lombok Plugin

LombokをIntelliJで補完したりするプラグイン。

Lombokについては、以下の記事を参照。

その他

あまり入れるようなプラグインはなかったな。オススメプラグインはない。

それはデフォルトでIntelliJ IDEAがとても便利ということだ。

思いついたら、追記するようにする。

01 Oct 2017, 03:56

タイタニックで決定木、交差検証、グリッドサーチ(Python)

はじめに

データサイエンティスト養成講座の第二回を受けてきました。

扱った内容は、

  • 決定木
  • クロスバリデーション
  • グリッドサーチ

講座では、Rを使うのだけれども、Pythonにもなれておきたいので、

講座の内容をPythonで復習しました。

ついでに、kaggleのタイタニック問題を決定木で解きました。

今回のコードは、githubにあげています。以下は、コードの抜粋です。

Pythonで決定木

Pythonで決定木を使うには、scikit-learnライブラリを使う。

from sklearn import tree
clf = tree.DecisionTreeClassifier(random_state=17)
clf.fit(X, y)

簡単!

クロスバリデーション with KFold

決定木は、max_depthパラメータを大きくすればするほど精度が上がっていくが、汎化性能が下がっていく。なので、クロスバリデーションという方法を使って、過学習が起こっていないかチェックすることが重要になる。

from sklearn.model_selection import KFold
from sklearn import metrics
from sklearn.metrics import accuracy_score
K = 5
kf = KFold(n_splits=K, shuffle=True, random_state=17)

score_train_tmp = 0
score_test_tmp = 0

for train_index, test_index in kf.split(X):
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

    # 構築データでモデル構築
    clf.fit(X_train, y_train)

    # 構築データの予測値
    pred_train = clf.predict(X_train)

    # 構築データのaccuracy
    auccuracy = accuracy_score(pred_train, y_train)

    #構築データのaccuracyを足していく
    score_train_tmp+=auccuracy

    #検証データの予測値
    pred_test = clf.predict(X_test)

    #検証データのaccuracy
    auccuracy = accuracy_score(pred_test, y_test)

    #検証データのaccuracyを足していく
    score_test_tmp+=auccuracy
score_train_tmp/K
0.82463676190176005

score_test_tmp/K
0.80247944259619608

構築データと検証データのスコアが近ければ、過学習が起こっていないと判断できる。これが乖離していると過学習が起こっているので、パラメータチューニングが必要。

グリッドサーチ

最適なパラメータをしらみつぶしにパラメータを組み合わせて探索していく方法をグリッドサーチという。普通はfor文を回してパラメータを変えてスコアを見ることで調整していく。しかし、scikit-learnには、GridSearchCVというグリッドサーチをするための専用のクラスが用意されている。

これをつかえば、煩わしいfor文のネストを書かずとも、複数パラメータに対して探索をすくことができる。

調べたいパラメータをまずは辞書で定義する。

from sklearn.model_selection import GridSearchCV

# use a full grid over all parameters
param_grid = {"max_depth": [2,4,6,8,10],
              "max_features": ['log2', 'sqrt','auto'],
              "min_samples_split": [2, 3, 5],
              "min_samples_leaf": [1,5,8],
              "criterion": ["gini", "entropy"]}

次に、GridSearchCVを読んで、グリッドサーチを実行する。

tree_grid = GridSearchCV(estimator=clf,
                 param_grid = param_grid,   
                 scoring="accuracy",  #metrics
                 cv = K,              #cross-validation
                 n_jobs =-1)          #number of core

tree_grid.fit(X,y) #fit

tree_grid_best = tree_grid.best_estimator_ #best estimator
print("Best Model Parameter: ",tree_grid.best_params_)
print("Best Model Score    : ",tree_grid.best_score_)
Best Model Parameter:  {&#39;criterion&#39;: &#39;gini&#39;, &#39;max_depth&#39;: 6, &#39;max_features&#39;: &#39;log2&#39;, &#39;min_samples_leaf&#39;: 8, &#39;min_samples_split&#39;: 2}
Best Model Score    :  0.812570145903

便利だ。素晴らしい。

しかし、このパラメータチューニングした結果をサイトにて提出しても、大したスコアは出ない。これは、特徴量エンジニアリングがそもそもしょぼいという問題がある。

30 Sep 2017, 07:00

[CentOS] IntelliJ IDEA では ibus に不具合があり日本語入力できないのでfcitxをつかおう

はじめに

仕事でVirtualBox上にCentOSを立ち上げてそのなかにIntelliJ IDEAを入れたのだけれども、

なぜか日本語入力ができなかったので、その調査と解決方法について書く。

環境

  • CentOS 7.4
  • IBus 1.5.3
  • fcitx 4.2.8

結論

調べたら、IntelliJ IDEAで IBus 1.5.11 以前のバージョンは不具合があるそうだ。

こんなメッセージが起動時に出る。

IBus prior to 1.5.11 may cause input problems. See IDEA-78860 for details.

おそらく、Jetbeans製のPhpStormやPyCharmでもどうようかと。

stackoverflowの解決方法にしたがって、ibus の最新版をいれたのだがibusがそれでもうまく動かなかった。おかしいな。

fcitxをつかおう

Input Methodは ibusだけではない。fcitxがある!というわけで、ibusをアンインストールしてfcitxをインストールすることにした。

以下、インストール手順です。

参考にしたリンク

fcitxをインストール

$ sudo yum install epel-release
$ sudo yum install -y \
fcitx-data.noarch \
fcitx-devel.x86_64 \
fcitx-gtk2.x86_64 \
fcitx-gtk3.x86_64 \
fcitx-libs.x86_64 \
fcitx-pinyin.x86_64 \
fcitx-qt4.x86_64 \
fcitx-qt5-devel.x86_64 \
fcitx-qt5.x86_64 \
fcitx-qw.x86_64 \
fcitx-table.x86_64 \
fcitx-anthy.x86_64
fcitx.x86_64

$ wget ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/updates/19/x86_64/fcitx-configtool-0.4.7-1.fc19.x86_64.rpm && sudo yum localinstall fcitx-configtool-0.4.7-1.fc19.x86_64.rpm

ibusの削除

$ sudo killall ibus-daemon
$ sudo yum remove ibus

fcitxをシステムのIMに設定

$ gsettings set org.gnome.settings-daemon.plugins.keyboard active false
$ imsettings-swich fcitx

~/.bashrcに追記

export GTK_IM_MODULE=fcitx
export QT_IM_MODULE=fcitx
export XMODIFIERS="@im=fcitx"

インライン入力を有効にする

これで一応はfcitxで IntelliJ IDEAが使えるようになったが、

入力すると、変換候補が下の方に出てきてしまう。インライン入力ができるように設定する。

  • fcitx -> 設定 -> アドオン
  • 拡張のチェックボックスをマーク
  • 検索窓からximを検索。Fcitx XIM Frontendを選択して設定を押す。
  • On The Spotを使うにチェックを入れ「OK」を押す

おわりに

解決方法に至るまでに6時間かかった。

ニッチなトラブルシューティングだけれども、同じように困っている人の助けになれば幸いです。