u-ryo's blog

various information for coding...

NullPointerException on Retrofit2 With Robolectric

| Comments

Android ApplicationをRobolectricでtestしていて、 どうにも困ったのでメモです。

状況は、Android Applicationで、 Robolectricを使っていて、 Retrofit2でPOSTしにいく部分(受け手はMockWebServer)のunit testで、 突然NullPointerExceptionになってsubscribeerrorに入ってしまう、というもの。 breakpointで追っていってもcall()で突如NPEに入ってしまって、 具体的にどこでNPEに陥っているのかよく分かりませんでした。 Googleで探してみると、 Stack Overflowにそれらしき投稿があり、 .observeOn(AndroidSchedulers.mainThread())LooperSchedulerなのでここでNPEになる、 だからRxAndroidPluginsregisterSchedulersHook()Schedulers.immediate()してやると良い、 と書いてあって、やったー! と思ったものの、効果なく。

結局そうではなくて、 MockWebServer使っているからhttp://localhost:NNNN/...に requestを改装しているせいなんですけど、 SecurityPolicy絡みのExceptionが裏で出ているようで、 以下のようなShadowを用意して@Config({shadows=...})に 書いてやればそのままですんなり行きました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import android.security.NetworkSecurityPolicy;

import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;

@Implements(NetworkSecurityPolicy.class)
public class ShadowNetworkSecurityPolicy {
    @Implementation
    public static NetworkSecurityPolicy getInstance() {
        try {
            Class<?> shadow = ShadowNetworkSecurityPolicy.class.forName("android.security.NetworkSecurityPolicy");
            return (NetworkSecurityPolicy) shadow.newInstance();
        } catch (Exception e) {
            throw new AssertionError();
        }
    }

    @Implementation
    public boolean isCleartextTrafficPermitted(String hostname) {
        return true;
    }
}

勿論、.subscribeOn(Schedulers.io())に対しては RxJavaHooks.setOnIOScheduler(s -> Schedulers.immediate()); した上で、です。

Groovy on Java9+

| Comments

いつも忘れちゃうので。 Java9以上のGroovyでmoduleが足りない、 具体的には、

1
2
Caught: java.lang.NoClassDefFoundError: Unable to load class groovy.xml.jaxb.JaxbGroovyMethods due to missing dependency javax/xml/bind/JAXBContext
java.lang.NoClassDefFoundError: Unable to load class groovy.xml.jaxb.JaxbGroovyMethods due to missing dependency javax/xml/bind/JAXBContext

と言われる時。 groovyではどうやって--add-modulesすればいいのかなって。

1
$ JAVA_OPTS='--add-modules=ALL-SYSTEM' groovy ...

RapidMiner

| Comments

今、会社の業務の一部(先月から0.75)でAI、機械学習をやっています。 題材としてSignate【練習問題】銀行の顧客ターゲティング をやっています。 業務時間中に色々試せるのはありがたいです。 PythonやR、kerasやGoogle Colaboratoryや Azure上のVM with GPUとか貰って試していたのですが、 結局RapidMiner使ってみました。 KSKのtutorialで勉強して、 色々と回せるようになりました。 確かに簡単ですねこれ。 でも、分かって来るとその限界や欠点も見えてきました。 また、課題に対する知見も溜まってきたので、忘れないうちに。

  • 基本的な流れは、Read CSV(train.csv)Cross Validation(Gradient Boosted TreesApply ModelPerformance(Binominal Classification))とRead CSV(test.csv)を合わせて→Apply ModelSelect AtributesWrite CSVRapidMiner flow inside cross validation
  • CSVのreadでは、まず最初の列のidをid、最後のyをlabelに指定、その他のattributesについては、yes/no 2値のものをbinominal、dayをintegerではなくpolynominalにするのがpointか。
  • dayとmonthから日付にしては? と思っていたのだが、pdateを見ると同じ月日でも何百日のものもあって複数yearsありそう、且つyearは特定できなさそう(365日毎でもない)
  • ならいっそday/monthをfeatureから落とすと精度が落ちる
  • せめてmonthだけでもdateとして認識できないかと試したがdate format指定をmmmでは出来なかった(小文字だから?)
  • ウリのAutoModelを試したところ、data file指定、fieldsのcorrelationから落とすattributesを指定、複数種類のmodelを実行、その後modelのparameterをいじれてsimulationが出来、modelに対する理解が進むというもの。AutoModelではGBoostよりDeepLearningが僅差で良かったがAUC 0.89程度
  • 自分でcross validationするようにして色々なmodelerを試したところ、Deep Learningも良かったがGradient Boosted TreesがAUC 0.90と最強だった
  • なのでGBoostでparameter tuningを。number of treesはdefaultの20からぐっと増やした方が。max depthはちょっとでも大きすぎるとダメ。learning rateもちょっとでも増やすと精度は落ちる
  • あんまり細かくtuningしても、結局randomizeの影響が大きい。小数点以下3桁程度のぶれは出てしまう。即ち、全く同じparametersで試しても結果が違う。reproducibleをcheckしても、cross validationの仕方が違うので、全てのrandom性を注意深く固定しないとならないし、randomのseedを探索する事になってしまうのもアホらしい
  • 仮にmodelingが出来たとして、これをserviceとして抜き出すのは難しそう。RapidMiner Serverを買えということか。そういうところで彼らは商売をしているのだろう。
  • GPUが無くても速い(30秒程度)のは、MultiThreadを使っているからか。topを見ていると気持ちいいくらいmulti coresを使っている。流石はJavaである。
  • よく言われるXGBoostというmodelerは無く、Gradient Boosted Treesがそれ相当
  • one-hot vector化しようと思ってNominal to Numericalを前処理に入れたら精度が落ちた。その出力をよく見たら、これ単に非numericalなattributesを落としてるだけ。何これ。じゃぁっていうんでNominal to Binomialにしてみても全く同じ。えー、RapidMinerではone-hot vector化、簡単に出来ないの!? 他の言語ではチョー簡単なのに。
  • RapidMiner、凄く簡単ですけど、これはinput dataがone fileで数万recordsしか無いから、でしょう。これが画像群だったらこうはいかないでしょう。

現時点で最良の結果(5/11/2018現在68位)

https://signate.jp/competitions/1/leaderboard#scoreboard

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
number of trees: 205
maximal depth: 5
min rows: 10.0
min split improvement: 0.0
number of bins: 20
learning rate: 0.1
sample rate: 0.8

PerformanceVector:
accuracy: 90.89% +/- 0.42% (mikro: 90.89%)
ConfusionMatrix:
True:	1	0
1:	1638	935
0:	1536	23019
AUC: 0.933 +/- 0.006 (mikro: 0.933) (positive class: 0)

これ以上AUCを上げるには、 RapidMinerじゃない何か、RかPythonを使って、 入力dataもone-hot vector化して、とかしないとならないような気がします。

Git Stash

| Comments

調べりゃすぐ出て来ますが。

Situation

localに取っておきたい一時的な変更があって、 でもそのせいでgit pullしてupdate出来ない、 git checkout ...してbranchを変えられない時。

Operation

  • git stash list -pで確認
  • git stashで退避
  • localの変更が無くなるので、この状態でgit checkou ...なりgit pull ...なり
  • git stash apply stash@{0}で直前のstashをapply
  • git stash dtrop stash@{0}で直前のstashをdrop←すぐ消さないと、後々残って何の変更なのかわからなくなる

Mock Web Server by WireMock on JHipster

| Comments

外部web siteのresponseのtestにWireMockを使ったのでメモ。

参考: WireMockを使って通信に関するテストをやってみよう

使う時はbuild.gradleで、

1
testCompile 'com.github.tomakehurst:wiremock-standalone:2.15.0'

して(wiremock-standaloneでないと、 jetty系のClassNotFoundExceptionに見舞われた)から、 test classの方で、

1
2
3
4
5
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
    :
    @Rule
    public WireMockRule wireMockRule = new WireMockRule(8089);

とし、各test method内で、

1
2
3
4
5
6
7
        stubFor(post(urlPathEqualTo("/any_path"))
                .withRequestBody(matching(".*arr_stn=%96%BC%8C%C3%89%AE.*"))
                .withRequestBody(matching(".*dep_stn=%93%8C%8B%9E.*"))
                .withRequestBody(matching(".*train=1.*"))
                .willReturn(aResponse()
                            .withStatus(200)
                            .withBodyFile("for_test.html")));

とstatic importした com.github.tomakehurst.wiremock.client.WireMock.stubForを使えば良い。 matching rule詳細は公式ページを。 post body requestに対して.withQueryParam("...", matching("..."))は効かなかった。 また、matchingなので全体にmatchingさせる必要があることに注意。 find系はなし。

test対象classでのqueryがここでの指定に合わなければ、404がthrowされる。

上記のように、test用に既に用意してあるhtml fileを.withBodyFile("...")で指定でき、 実体はsrc/test/resources/__files/に置く。 .withBodyFile(...)は公式Docに無く、JavaDoc APIも無いので、 Github上のsource codeを眺めて見つけた。

こうしたmappingをsrc/test/resources/mappings/以下にanyName.jsonを置いて、 requestとresponseを指定できるのだが、 それだと「こういうrequest bodyの時はこのようなresponse、 こっちの時はこれこれのresponse」というように、 複数の条件を書くことが出来なかった (書いてみると、最後のrequest/response条件しか効かず。 ならばと複数fileに分けてみたら、 file名alphabeticalで最後のrequest/responseしか効かず)。 なので、Javaでstub条件書いて各test method内に書くしか無い?

これはSpringの話だが、 testの時だけlocalhost:8089を見るようにするには、 〜.config.ApplicationProperties classにfield定義、setter/getterのaccessorを付け、src/main/resources/config/application.ymlapplication:以下に通常時の値を、src/test/resources/config/application.ymlapplication:以下にtest時の値(この場合はhttp://localhost:8089/any_path)を記述。 実際に使うには、

1
2
    @Autowired
    private ApplicationProperties properties;

とDIさせて、properties.get〜()と普通にgetterでget。

AssertJ、method chainで書けるので、 static importがassertThatだけで済んでいいですね。

これはJHipsterの話?(Springかなぁ?)で、 Test Class内で@Autowiredしてるservice classのmockが必要になったら、 特にbuild.gradeに記述せずともimport org.mockito.Mock;して mockitoの@Mockが使えるんですね。 そう言えば、元々import org.mockito.MockitoAnnotations;されてますね。