mike-neckのブログ

Java or Groovy or Swift or Golang

OpenCV/Java 環境構築メモ

JavaOpenCVをごにょごにょやるのに、少し手間取ったのでメモ。

インストールする環境などは次のとおり。

OpenCVのインストール

(1) pythonbrewでインストール

$ brew install python
$ brew install python3

(2) numpyをインストールするためにgccをインストール

$ brew install gcc

numpyがgfortrunに依存していて、gfortrunがgccに吸収されているので、インストールされる。

なお、makeに時間がかかる(1時間くらい)

(3) numpyをインストール

$ brew install numpy --with-python3

(4) opencv3をインストール

$ brew install opencv3 --with-python3 --with-java

Javaでいじる場合には--with-javaをいれておく

Pythonでインストールの確認をするので、次のコマンドをうっておく

$ brew link opencv3 --force

インストール後にpython3を起動してimport cv2をおこなって、エラーが出なければインストール成功

Javaプロジェクト

(1) libopencv_java310.so のファイル名を変更

github.com

OpenCVに同梱されているネイティブライブラリーのlibopencv_java310の拡張子がsoになっているので、dyLibに変更する

$ cd /usr/local/Cellar/opencv3/3.1.0_3/share/OpenCV/java
$ sudo mv libopencv_java310.so libopencv_java310.dyLib

(2) opencv_310.jarからjavaファイルのみを取り出してサブプロジェクトにする

OpenCVに同梱されているopencv_310.jarにはいっているclassファイルがJava9eaでコンパイルされていて、IntelliJ IDEAでクラスファイルを認識できない。そこでopencv_310.jarからjavaファイルを取り出してサブプロジェクトを作り、メインのほうから依存するようにプロジェクトを構成する。

settings.gradle

include 'opencv'

build.gradle

dependencies {
    // サブプロジェクトに依存
    compile project(':opencv')
}

// jarからjavaファイルを取り出す
task copySource(type: Copy) {
    def jarFile = file('/usr/local/Cellar/opencv3/3.1.0_3/share/OpenCV/java/opencv-310.jar')
    from zipTree(jarFile)
    exclude '**/*.class'
    exclude 'META-INF/*'
}

// サブプロジェクトの設定
project(':opencv') {
    apply plugin: 'java'
}

(3) プログラム実行時の起動オプション

ネイティブライブラリーのパスをjava.library.pathに設定して起動する

-Djava.library.path=/usr/local/Cellar/opencv3/3.1.0_3/share/OpenCV/java

System.setPropertyでやってみたが、その場合にはネイティブライブラリーをうまく探しだしてくれなかった

おわり

Javaの機械学習ライブラリーでFizzBuzzしてみた

FizzBuzzをするのにTensorFlowというネタツイートがあったので、「機械学習 FizzBuzz」で検索したらすでにいくつかあったので、真似してみたくなった。ちなみに機械学習のことは全然わかってません。

qiita.com

blog.amedama.jp

github.com


ライブラリー/フレームワーク

EncogというJava機械学習ライブラリーを使いました(TensorFlow使いたかった…)。

EncogはJeff Heatonという方が開発している機械学習ライブラリーで、サポートベクターマシンニューラルネットワークベイジアンネットワークなどいくつかのアルゴリズムをサポートしているJava/C#フレームワークだそうです。ドキュメントもしっかりしているので、初心者でも使いやすいライブラリーとなっています。

github.com


導入

dependencies {
  compile 'org.encog:encog-core:3.3.0'
}

学習データ

201〜1000を学習データとして用いました。

特徴量は「3で割り切れた場合1.0、割り切れなかった場合0.0」「5で割り切れた場合1.0、割り切れなかった場合0.0」という二つの量を設定しました。

//入力の数値
data class InputNumber(
        val num: Int, val fizz: Boolean, val buzz: Boolean)

//Intから入力の数値に変換する
val toInput: (Int) -> InputNumber = {
    val fizz = it % 3 == 0
    val buzz = it % 5 == 0
    InputNumber(it, fizz, buzz)
}

Qiitaの記事のSVMFizzBuzzと同じように、FizzBuzzのフィルター、Buzzのフィルター、Fizzのフィルターを用意し、入力された数値がそれぞれのフィルターに適合する場合は1.0、適合しない場合0.0となるような教師データを用意します。たとえばFizz用のフィルターには次のような学習データを与えてトレーニングします。

数値 特徴量 出力
201 [1.0, 0.0] 1.0
202 [0.0, 0.0] 0.0
203 [0.0, 0.0] 0.0
204 [1.0, 0.0] 1.0
205 [0.0, 1.0] 0.0
//Fizz/Buzzをあらわす
enum class FizzBuzz {
    FIZZ,
    BUZZ,
    FIZZ_BUZZ,
    NONE
}

//入力値のFizzBuzz結果
fun InputNumber.toFizzBuzz(): FizzBuzz {
    return when(Pair(fizz, buzz)) {
        Pair(true, true)  -> FizzBuzz.FIZZ_BUZZ
        Pair(false, true) -> FizzBuzz.BUZZ
        Pair(true, false) -> FizzBuzz.FIZZ
        else              -> FizzBuzz.NONE
    }
}

//FizzBuzzの教師データ変換
fun FizzBuzz.ideal(fizzBuzz: FizzBuzz): DoubleArray =
        if (this == fizzBuzz) doubleArrayOf(1.0)
        else doubleArrayOf(0.0)

//学習データ
data class FizzBuzzStudy(
        val input: InputNumber,
        val fizzBuzz: FizzBuzz)

//学習データの入力値を取り出す
fun FizzBuzzStudy.inputArray(): DoubleArray =
        booleanArrayOf(input.fizz, input.buzz).toDoubleArray()

//学習データの教師データを取り出す
fun FizzBuzzStudy.idealDataArray(): DoubleArray =
        input.toFizzBuzz().ideal(fizzBuzz)

学習の実行

今回はBasicNetworkというネットワークを使います。これはEncogのQuick Start GuideにあるXORの学習で使われているものです。

学習エラーが0.01以下になるまで繰り返し学習を行います。

//dataは201〜1000までの学習データを保持するオブジェクト
//fizzBuzzはフィルターをあらわすFizzBuzz(FizzBuzz専用フィルターかFizz専用フィルターか)
class Filter(val data: MLDataSet, val fizzBuzz: FizzBuzz) {

    val network = BasicNetwork()

    init {
        network.addLayer(BasicLayer(null, true, 2))
        network.addLayer(BasicLayer(ActivationSigmoid(), true, 4))
        network.addLayer(BasicLayer(ActivationSigmoid(), false, 1))
        network.structure.finalizeStructure()
        network.reset()

        val training = ResilientPropagation(network, data)
        for (t in 1..30) {
            training.iteration()
            if (training.error < 0.01) break
        }
        training.finishTraining()
    }

    //etc...
}

フィルター

学習が終わると、Filternetworkというオブジェクトはcomputeというメソッドで、入力値(特徴量)が適合する場合に1に近い数値を返すようになります。

これをFizzBuzzの場合、Buzzの場合、Fizzの場合の3回おこなって、FizzBuzzの判断をおこないます。

class Filter(val data: MLDataSet, val fizzBuzz: FizzBuzz) {
    //一部省略

    //入力値から値を予測する
    private fun compute(input: MLData): MLData = network.compute(input)

    //入力値が適合するかチェックする
    fun match(input: InputNumber): Boolean =
        compute(BasicMLData(input.toDataArray())).getData(0).round() == 1
}

//数値をFizzBuzzに変換する
fun InputNumber.fizzBuzz(filters: List<Filter>): String {
    val found = filters.find { it.match(this) }
    return when(found?.fizzBuzz) {
        FizzBuzz.FIZZ_BUZZ -> FizzBuzz.FIZZ_BUZZ.name
        FizzBuzz.BUZZ      -> FizzBuzz.BUZZ.name
        FizzBuzz.FIZZ      -> FizzBuzz.FIZZ.name
        else               -> "$num"
    }
}

プログラム実行

学習データは201〜1000、テストするデータは1〜40として機械学習によるFizzBuzzを実行してみます。

object StudyRange {
    val STUDY_START = 201
    val STUDY_END = 1000
}

fun main(args: Array<String>) {
    val inputList: List<InputNumber> =
            StudyRange.STUDY_START.rangeTo(StudyRange.STUDY_END).map(toInput)
    val studyDataList: List<StudyData> = fizzBuzzList().map { StudyData(inputList, it) }
    val filters: List<Filter> = studyDataList.map { Filter(it.dataSet(), it.fizzBuzz) }

    val testData: List<InputNumber> = (1..40).map(toInput)
    testData.map {
        "${it.num} -> ${it.fizzBuzz(filters)} (correct: ${it.correct()})"
    }.forEach{
        println(it)
    }
    Encog.getInstance().shutdown()
}

実行結果

1 -> 1 (correct: 1)
2 -> 2 (correct: 2)
3 -> FIZZ (correct: FIZZ)
4 -> 4 (correct: 4)
5 -> BUZZ (correct: BUZZ)
6 -> FIZZ (correct: FIZZ)
7 -> 7 (correct: 7)
8 -> 8 (correct: 8)
9 -> FIZZ (correct: FIZZ)
10 -> BUZZ (correct: BUZZ)
11 -> 11 (correct: 11)
12 -> FIZZ (correct: FIZZ)
13 -> 13 (correct: 13)
14 -> 14 (correct: 14)
15 -> FIZZ_BUZZ (correct: FIZZ_BUZZ)
16 -> 16 (correct: 16)
17 -> 17 (correct: 17)
18 -> FIZZ (correct: FIZZ)
19 -> 19 (correct: 19)
20 -> BUZZ (correct: BUZZ)
21 -> FIZZ (correct: FIZZ)
22 -> 22 (correct: 22)
23 -> 23 (correct: 23)
24 -> FIZZ (correct: FIZZ)
25 -> BUZZ (correct: BUZZ)
26 -> 26 (correct: 26)
27 -> FIZZ (correct: FIZZ)
28 -> 28 (correct: 28)
29 -> 29 (correct: 29)
30 -> FIZZ_BUZZ (correct: FIZZ_BUZZ)
31 -> 31 (correct: 31)
32 -> 32 (correct: 32)
33 -> FIZZ (correct: FIZZ)
34 -> 34 (correct: 34)
35 -> BUZZ (correct: BUZZ)
36 -> FIZZ (correct: FIZZ)
37 -> 37 (correct: 37)
38 -> 38 (correct: 38)
39 -> FIZZ (correct: FIZZ)
40 -> BUZZ (correct: BUZZ)

どうやらうまくいったようです。

全体のコードはgistにあります。

機械学習でFizzBuzz · GitHub

Gradle2.11の新機能を試してみた(1)

Gradle2.11でJavaソフトウェアモデルに対してJUnitによるテストがサポートされたので試してみました。

基本

ディレクトリー構成は以下のとおりです。

root
 └─src
    ├─data
    │  └─java
    ├─data.test
    │  └─java
    ├─exceptions
    │  └─java
    └─functions
       └─java

使用するJavaのバージョンはJava8、JUnitのバージョンは4.12です。また、各ライブラリーは次のような機能を提供しています。

  • exceptions - 例外クラスを提供。
  • functions - 関数型インターフェースを提供。exceptionsライブラリーに依存。
  • data - データ型を提供。functionsライブラリーに依存。
  • data.test - dataライブラリーのテスト。

このようなJavaソフトウェアモデルのビルドファイルは次のようになります。

plugins {
  id 'jvm-component'
  id 'java-lang'
  //JavaソフトウェアモデルにてJUnitを利用するためのプラグイン
  id 'junit-test-suite'
}
repositories {
  jcenter()
}
model {
  testSuites {
    //data.test ディレクトリーにテストを配置するためにシングルクォートで囲む
    'data.test'(JUnitTestSuiteSpec) {
    //JUnitのバージョン指定
      jUnitVersion '4.12'
      //テスト対象のライブラリーを指定
      testing $.components.data
    }
  }
  components {
    exceptions(JvmLibrarySpec) {
      targetPlatform 'java8'
    }
    functions(JvmLibrarySpec) {
      targetPlatform 'java8'
      api {
        exports 'com.sample.func.api'
        dependencies {
          library 'exceptions'
        }
      }
    }
    data(JvmLibrarySpec) {
      targetPlatform 'java8'
      api {
        exports 'com.sample.data.api'
        dependencies {
          library 'functions'
        }
      }
    }
  }
}

tasksタスクを実行してタスクの一覧を見てみます。

$ gradle tasks
…前略…
Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
clean - Deletes the build directory.
data.testDataJarBinary - Assembles Test suite 'data.test:dataJarBinary'.
dataJar - Assembles Jar 'data:jar'.
exceptionsJar - Assembles Jar 'exceptions:jar'.
functionsJar - Assembles Jar 'functions:jar'.
…中略…
Verification tasks
------------------
check - Runs all checks.
data.testDataJarBinaryTest - Runs test suite 'data.test:dataJarBinary'.
…以下略…

data.testDataJarBinaryというタスクがテストのコンパイルで、data.testDataJarBinaryTestというタスクがテストタスクです。

ではdata.testDataJarBinaryTestタスクを実行してみます。

$ gradle data.testDataJarBinaryTest
:compileExceptionsJarExceptionsJava
:exceptionsApiJar
:compileFunctionsJarFunctionsJava
:functionsApiJar
:compileDataJarDataJava
:compileData.testDataJarBinaryData.testJava
:createExceptionsJar
:createFunctionsJar
:data.testDataJarBinaryTest

BUILD SUCCESSFUL

Total time: 13.984 secs

テストのレポートはbuild/reports/data.test/dataJar/testsディレクトリーに出力されます。なお、レポートの内容はGradleでお馴染みのものです。また、JUnitxmlファイルはbuild/test-results/data.test/dataJarディレクトリーの下に出力されます。

f:id:mike_neck:20160221205738p:plain

テスト対象のdataライブラリーはjarファイルが作成されていませんし、data.testの方もjarファイルは作成されません。

JenkinsでGradleを利用する場合は大抵buildタスクを実行すると思いますので、buildもやってみましょう。

$ gradle clean build
:clean
:compileExceptionsJarExceptionsJava
:exceptionsApiJar
:compileFunctionsJarFunctionsJava
:functionsApiJar
:compileDataJarDataJava
:createDataJar
:dataApiJar
:dataJar
:createExceptionsJar
:exceptionsJar
:createFunctionsJar
:functionsJar
:assemble
:compileData.testDataJarBinaryData.testJava
:data.testDataJarBinaryTest
:check
:build

BUILD SUCCESSFUL

Total time: 5.025 secs

buildタスクでdata.testDataJarBinaryTestタスクが実行されているのがわかります。

応用

応用というほどではありませんが、今回はdataライブラリーに対してテストを実行しましたが、次のようにビルドファイルを用意して、src/funcsionsTests/java以下のディレクトリーにテストを記述することで、functionsライブラリーに対するテストも行えるようになります。

model {
  testSuites {
    'data.test'(JUnitTestSuiteSpec) {
      jUnitVersion '4.12'
      testing $.components.data
    }
    functionsTests(JUnitTestSuiteSpec) {
      jUnitVersion '4.12'
      testing $.components.functions
    }
  }
  //以下略
}

結論

まあ、現状のJavaプロジェクトで問題ないわけだし、JavaソフトウェアモデルにしなくてもJUnitだけでなく、TestNGも使えるし、webプロジェクトはJavaソフトウェアモデルでは作れないし、現状のjavaプラグインでいいんじゃね?という話でした。

Gradle2.11を試すシリーズ、次回はないと思います。

Gradle2.11リリースノート意訳(また途中)

gradleのバージョン2.11がとっくの昔に出ていたので、リリースノートを意訳しておきました。

原文はこちら


Gradle2.11ではソフトウェアモデルとIDE連携およびコンティニュアスビルドを強化しています。

ソフトウェアモデル改善はGradleの現在の目標となっています。ネイティブ対応とPlayフレームワーク対応から始まったソフトウェアモデルはJavaのフルサポートを目標として開発しています。今回は新しいJavaプラグインでのJUnitによるテストのサポートとコンパイルに関するジョブの最適化を行っています。こうした改善に伴い新しいソフトウェアモデルによるプラグイン開発も改善されています。

ソフトウェアモデルだけでなく、既存のJavaプロジェクトもGradle2.11の恩恵を受けられます。IDE連携が改善されたことにより、IntelliJ IDEAやEclipseなどのIDEでGradleプロジェクトを扱いやすくなっています。IDEのプロジェクトファイルとツーリングAPIの改善によりこれがもたらされています。

ビルド中の変更監視によりコンティニュアスビルドはより信頼できるものとなっています。これはユーザーにこれこそがGradleで開発することであると実感できる本当におすすめの機能です。

今回もGradleコミュニティのおかげでGradle2.11をリリース出来ました。主に次の箇所でコントリビュートを受けています。

新機能など

ソフトウェアモデルでのプラグイン開発サポート

プラグインがマネージド型をカスタムソースセットとして扱えるようになった

LanguageSourceSet型は@Managedを継承した型として扱えるようになった。またこれはデフォルト実装を提供しなくてもよい。

@Managed
interface MarkdownSourceSet extends LanguageSourceSet {
    boolean isGenerateIndex()
    void setGenerateIndex(boolean generateIndex)
    boolean isSmartQuotes()
    void setSmartQuotes(boolean smartQuotes)
}
class RegisterMarkdown extends RuleSource {
    @LanguageType
    void registerMarkdown(LanguageTypeBuilder<MarkdownSourceSet> builder) {
        builder.setLanguageName("Markdown")
    }
}
apply plugin: 'language-base'
apply plugin: RegisterMarkdown

model {
    md(MarkdownSourceSet) {
        generateIndex = true
    }
}

A @ComponentBinaries rule can create binaries for any ComponentSpec instance

@ComponentBinariesアノテーションをつけたクラスからバイナリーを生成できるようになったらしい…(よくわかってない)

Javaモデルの改善

テストサポート

Javaモデルは新たにソフトウェアコンポーネントとしてJUnitテストスイートを宣言できるようになった。スタンドアローンコンポーネントあるいはテスト管理下のコンポーネントとして。詳しくはユーザーガイドを参照とのこと。

コンパイル回避の改善

今バージョンのGradleからABI(Application Binary Interface)が変更しないライブラリーを利用するライブラリーの再コンパイルを回避するようになった。バージョン2.9からはライブラリーがAPIを宣言した場合に、スタブAPIのjarファイルをGradleは生成するようになっていた。これによりABIが変更しないライブラリーを利用するライブラリーを再コンパイルを回避することができる。今バージョンのGradleではAPIを宣言しないライブラリーにもこの機能を拡大適用する。例えばライブラリーAがライブラリーBに依存している場合、次のケースで再コンパイルが必要なくなる。

  • ライブラリーBにプライベートなメソッドが追加された場合
  • ライブラリーBのメソッドの内容が変更された場合
  • ライブラリーBのメソッドの順番が変更された場合

この機能はローカルライブラリーのみに適用され、外部依存ライブラリーには適用されない。詳しくはユーザーガイドを参照。

IDE連携

GradleのIDE連携は二通りある。一つはプロジェクトファイル生成する方法ともう一つはツーリングAPIを通じたプロジェクトの読み込みである。今バージョンではこのサポートが改善された。プロジェクトファイル生成した場合はすぐに試せるが、gradleファイル読み込みの場合はIntelliJ IDEAあるいはBuildship(EclipseのGradle用プラグイン)の更新が必要となる。

生成されたIDEAのファイルにあるJavaの言語レベルが正しくなる

これまでideaプラグインで生成されるプロジェクトファイルはルートプロジェクトのsourceCompatibilityだけが考慮されていて、サブプロジェクトは考慮されていなかった。これが修正されてすべてのサブプロジェクトのsourceCompatibilityの中で最も高いものが言語レベルとして設定される。なおDSLにてidea.project.languageLevelが設定されていた場合はssourceCompatibilityは考慮されない。言語レベルとして設定された値はiprファイル、imlファイルを生成するときに使われる。またツーリングAPIから取得する場合にも利用される。

IntelliJ IDEAにツーリングAPIからJavaの設定情報が渡される

ツーリングAPIはGradleビルドがIntelliJ IDEAに取り込まれた時に、IdeaProjectモデルおよびIdeaModuleモデルを提示するようになる。これらのモデルにはJavaの言語設定情報が含まれていなかったが、ビルドと同じ状態をIDEでも再現できるようになる。Java言語設定はIdeaProject.getJavaLanguageSettings()あるいはIdeaModule.getJavaLanguageSettings()から取得できるようになる。

Eclipse.classpathファイルはJavaランタイム情報を保持するようになる

eclipseClasspathタスクで生成される.classpathファイルはEclipseのデフォルトランタイムを指示していたが、具体的にJavaランタイムのバージョンを指定するようになる。ランタイムの名前はEclipseのデフォルトの方法に従い、javaプラグインtargetCompatibilityの値からJavaランタイムの名称を決定するようになる。

ランタイムの名称を変更する場合はEclipsejdtモデルにあるjavaRuntimeNameの値を用いる。

eclipse {
  jdt {
    javaRuntimeName = "JavaSE-1.8"
  }
}

ツーリングAPIからEclipseJava言語設定に関してより多くの情報が渡されるようになる

ターゲットとなるバイトコードのバージョン情報がeclipseに渡されるようになる。

これらEclipseに関する変更はBuildshipの次のバージョンから利用可能になる。

コンティニュアスビルドによるファイルの自動変更検知

Gradle2.5以来のコンティニュアスビルドはビルド終了後にファイル変更を検知していたが、今バージョンからはビルド途中でもファイルの変更を検知して再ビルドする。

TestNGの実行順序サポート

preserveOrderおよびgroupByInstancesの2つのプロパティによってテスト実行順序を制御できるようになる。詳しくはマニュアルを参照。

test {
  useTestNG {
    preserveOrder true
    groupByInstances true
  }
}

Javaプロジェクト新規作成時のテスティングフレームワーク選択

initタスクでGradleプロジェクトを作る際にテスティングフレームワークとしてJUnitの代わりにSpockあるいはTestNGを指定することができるようになった。以下のとおり指定する。

gradle init --type java-library --test-framework spock
gradle init --type java-library --test-framework testng

Ivyディスクリプター生成時のexcludeサポート

GradleのIvyパブリッシュプラグインでivy形式のアーティファクトを発行できるが、今回のリリースでexcludeする依存性の情報をivy.xmlファイルに出力することが可能になった。

TwirlのJavaデフォルトインポート

GradleのPlayプラグインではTwirlテンプレートのコンパイルができるわけだが、Scalaのデフォルトインポートが使われると考えていたが、それをJavaのデフォルトインポートを使うかデベロッパーが選択できるようになった。

model {
  components {
    play {
      twirlTemplates {
        defaultImports = TwirlImports.JAVA
      }
    }
  }
}

Separate convention plugins for native unit testing

Playスタンドアローン版配布の修正

バグ修正

非推奨になったやつ

ビルドが壊れる可能性のあるもの

既知の問題