1. 概要

教師あり学習は機械学習のサブセットです。 これは、ラベル付けされた経験的データを使用して機械学習モデルをトレーニングすることで構成されます。 言い換えれば、データは経験から収集されました。

このチュートリアルでは、Kotlinで教師あり学習を適用する方法を学習します。 2つのアルゴリズムを見ていきます。 1つは単純でもう1つは複雑です。 その過程で、正しいデータセットの準備についても説明します。

2. アルゴリズム

教師あり学習モデルは、機械学習アルゴリズムに基づいて構築されています。 複数の種類のアルゴリズムが存在します。

まず、いくつかの一般的な概念を見ていきましょう。その後、よく知られているアルゴリズムのいくつかを続けていきます。

2.1. データセットの準備

まず、データセットが必要です。 完全なデータセットには事前にラベルが付けられており、その機能は関連性があり、モデルへの入力として提供されるように変換されます。

残念ながら、完全なデータセットは存在しません。 したがって、データを準備する必要があります。

教師あり学習を行う場合、データの準備は非常に重要です。 まず、 Wine QualityDatasetに触発されたデータセットを見てみましょう。

タイプ 酸度 二酸化炭素 pH
.27 45 3
白い .3 14 3.26
.28 47 2.98
白い .18 3.22
16 3.17

まず、データセット内の欠落しているセルを処理することから始めましょう。 データセット内の欠落データを処理するための複数の手法が存在します。

たとえば、私たちの場合、 不足しているタイプのワインを含む行を削除します。これは、他の機能を説明するのに役立つため、ここではタイプが重要であるためです。 一方で、 また、欠落している数値を、機能の既存の値の平均に置き換えます。

タイプ 酸度 二酸化炭素 pH
.27 45 3
白い .3 14 3.26
白い .18 31 3.22
.26 16 3.17

明確にするために、列の小数精度を考慮してそれらを切り上げることも確認しました。

次に、カテゴリを数値に変換して続けましょう。 この機能はカテゴリ機能と見なされます。 欠測データに関しては、複数の手法がカテゴリデータに適用されます。

この場合、前述のように、値を数値に置き換えます。

タイプ 酸度 二酸化炭素 pH
0 .27 45 3
1 .3 14 3.26
1 .18 31 3.22
0 .26 16 3.17

データセットを準備するための最後のステップは、特徴のスケーリングです。明確にするために、特徴のスケーリングは、同じスケールの値(通常は[0、1]または[-1、1)で複数の特徴を取得するプロセスです。 ]

たとえば、データセットの場合、最小-最大スケーリングを使用します。

最小-最大スケーリングは、特定の列の最小値から最大値までのスケールを作成することで構成される手法です。

たとえば、二酸化物の列では、 14 が最小値として0になり、47が1になります。 したがって、他のすべての値は次の間に収まります。

タイプ 酸度 二酸化炭素 pH
0 .75 .94 .07
1 1 0 1
1 0 .52 .86
0 .67 .06 .68

最後に、データセットが準備されたので、モデルに焦点を当てることができます。

2.2. モデル

モデルは、データセットから入力を取得して出力を生成するアルゴリズムです。 さらに、教師あり学習アルゴリズムの詳細については、この紹介記事を参照してください。

人工ニューラルネットワークの例を取り上げて、次の概念をいくつかの例で説明しましょう。

機械学習モデルで作業するときは、さまざまな種類のパラメーターについて話す傾向があります。 モデルが持つべき最適な動作に合うようにモデルがトレーニングしている間に、それらは進化します。 たとえば、トレーニング中に調整される入力データの乗数について考えます。 いくつかの可能なpパラメータは、図の赤い値で表されています。

ハイパーパラメータと呼ばれる他の種類のパラメータが存在します。 いくつかの可能なハイパーパラメータは、図の青い値で表されています。 これらのパラメータは、トレーニング期間の前に定義されます。 たとえば、人工ニューラルネットワークに必要なニューロンの数について考えてみましょう。 別の種類のハイパーパラメータは、使用するアクティブ化関数の種類または使用する損失関数の種類です。

次に、線形回帰と人工ニューラルネットワークの2種類の機械学習アルゴリズムを見てみましょう。 もちろん、他のアルゴリズムも存在します(SVM、ロジスティック回帰、決定木、ナイーブベイズなど)が、この記事ではそれらに焦点を当てません。

2.3. 線形回帰

線形回帰は、その名前が示すように、回帰アルゴリズムです。 複数のバリエーションがあります。

  • 単純線形回帰は、あるスカラー変数を別のスカラー変数で説明します。 たとえば、家の価格は平方フィートの数で説明できます
  • 重回帰も1つのスカラー変数を説明します。 ただし、今回は、1つの変数を入力として使用する代わりに、複数の変数を受け取ります。 住宅価格予測の同じケースでは、部屋の数、バスルーム、学校までの距離、公共交通機関のオプションなどの追加の変数を考慮に入れます。
  • 多項式線形回帰は、重回帰と同じ問題を解決しますが、それが描く予測は常に進化しているわけではありません。 住宅の価格は、たとえば、常にではなく、指数関数的に上昇する可能性があります
  • 最後に、一般線形モデルは、1つではなく複数の変数を同時に説明します

他のタイプの線形回帰が存在しますが、あまり一般的ではありません。

簡単に言えば、線形回帰はデータセット(青い点)を取得し、新しいデータから推測を推定するためにそれらを介して妥当な線(赤)を投影します。

2.4. 人工ニューラルネットワーク

人工ニューラルネットワークは、より複雑な機械学習アルゴリズムです。

彼ら人工ニューロンの1つまたは複数の層で構成され、各層は1つまたは複数のニューロンで構成されます。 ニューロンは、入力を受け入れ、他のニューロンまたは最終出力への出力をフィードフォワードする数学関数です。

フィードフォワードが実行された後、関数の変数を修正するためにバックプロパゲーションが実行されます。 バックプロパゲーションは、モデルのコストを下げ、定義上、その精度と精度を高めるために必要です。 これは、トレーニングデータセットを使用してトレーニング期間を通じて行われます。

ANNは、回帰と分類の問題を解決するために使用できます。 彼らは通常、画像認識、音声認識、医療診断、機械翻訳などに優れています。

入力ニューロンと出力ニューロン、およびニューロンの隠れ層(高密度層とも呼ばれる)で構成される人工ニューラルネットワークを視覚化することができます。

3. ネイティブKotlinを使用したデモ

まず、Kotlinまたはその他の言語でネイティブにモデルを作成する方法を説明します。 単純な線形回帰を例にとってみましょう。

覚えておくと、単純な線形回帰は、独立変数を使用して従属変数を予測するモデルです。 たとえば、子供の数に応じて、家族に必要なミルクの量を予測することができます。

3.1. 式

単純な線形回帰の式と、必要な変数の値を取得する方法を見てみましょう。

# Variance
variance = sumOf(x => (x - meanX)²)
# Covariance
covariance = sumOf(x, y => (x - meanX) * (y - meanY))
# Slope
slope = coveriance / variance
# Y Intercept
yIntercept = meanY - slope x meanX
# Simple Linear Regression
dependentVariable = slope x independentVariable + yIntercept

3.2. フォーミュラのKotlin実装

ここで、この擬似コードをKotlinに変換し、年収の中央値を数千年の経験で表すデータセットを使用してみましょう。

// Dataset
val xs = arrayListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val ys = arrayListOf(25, 35, 49, 60, 75, 90, 115, 130, 150, 200)
// Variance
val variance = xs.sumByDouble { x -> (x - xs.average()).pow(2) }
// Covariance
val covariance = xs.zip(ys) { x, y -> (x - xs.average()) * (y - ys.average()) }.sum()
// Slope
val slope = covariance / variance
// Y Intercept
val yIntercept = ys.average() - slope * xs.average()
// Simple Linear Regression
val simpleLinearRegression = { independentVariable: Int -> slope * independentVariable + yIntercept }

モデルが作成されたので、それを使用して値を予測できます。 たとえば、2.5または7.5年の経験を持つ人は、いくら稼ぐことができますか? それをテストしてみましょう:

val twoAndAHalfYearsOfExp = simpleLinearRegression.invoke(2.5) // 38.99
val sevenAndAHalfYearsOfExp = simpleLinearRegression.invoke(7.5) // 128.84

3.3. 結果の評価

結果は、期待される動作に対応しているようです。 しかし、このステートメントをどのように評価できますか?

損失関数(この場合はR²)を使用して、長年の経験が従属変数の給与をどの程度説明しているかを計算します。

# SST
sst = sumOf(y => (y - meanY)²)
# SSR
ssr = sumOf(y => (y - prediction)²)
# R²
r² = (sst - ssr) / sst

Kotlinの場合:

// SST
val sst = ys.sumByDouble { y -> (y - ys.average()).pow(2) }
// SSR
val ssr = xs.zip(ys) { x, y -> (y - simpleLinearRegression.invoke(x.toDouble())).pow(2) }.sum()
// R²
val rsquared = (sst - ssr) / sst

最後に、R²に対して 0.95 を取得します。これは、長年の経験が従業員の給与の95%の精度で説明されることを意味します。 したがって、これは間違いなく予測に適したモデルです。 他の変数は、たとえば残りの5%を説明するための交渉スキルまたは認定の数である可能性があります。 または、おそらくランダム性。

4. Deeplearning4jを使用したデモ

このデモでは、 Zalando MNIST データセットを使用して、畳み込みニューラルネットワークをトレーニングします。 データセットは、靴、バッグ、その他8種類の衣類の28×28枚の画像で構成されています。

4.1. Mavenの依存関係

まず、Deeplearning4j依存関係を単純なMavenKotlinプロジェクトに追加することから始めます。

<dependency>
    <groupId>org.deeplearning4j</groupId>
    <artifactId>deeplearning4j-core</artifactId>
    <version>1.0.0-beta5</version>
</dependency>

また、nd4j依存関係を追加しましょう。 ND4Jは、多次元行列計算を実行するためのAPIを提供します。

<dependency>
    <groupId>org.nd4j</groupId>
    <artifactId>nd4j-native-platform</artifactId>
    <version>1.0.0-beta5</version>
</dependency>

4.2. データセット

必要な依存関係を追加したので、データセットをダウンロードして準備しましょう。 ZalandoMNISTGitHubページから入手できます。 ピクセルベクトルの最後にラベルを追加して準備します。

private const val OFFSET_SIZE = 4 //in bytes
private const val NUM_ITEMS_OFFSET = 4
private const val ITEMS_SIZE = 4
private const val ROWS = 28
private const val COLUMNS = 28
private const val IMAGE_OFFSET = 16
private const val IMAGE_SIZE = ROWS * COLUMNS

fun getDataSet(): MutableList<List<String>> {
    val labelsFile = File("train-labels-idx1-ubyte")
    val imagesFile = File("train-images-idx3-ubyte")

    val labelBytes = labelsFile.readBytes()
    val imageBytes = imagesFile.readBytes()

    val byteLabelCount = Arrays.copyOfRange(labelBytes, NUM_ITEMS_OFFSET, NUM_ITEMS_OFFSET + ITEMS_SIZE)
    val numberOfLabels = ByteBuffer.wrap(byteLabelCount).int

    val list = mutableListOf<List<String>>()

    for (i in 0 until numberOfLabels) {
        val label = labelBytes[OFFSET_SIZE + ITEMS_SIZE + i]
        val startBoundary = i * IMAGE_SIZE + IMAGE_OFFSET
        val endBoundary = i * IMAGE_SIZE + IMAGE_OFFSET + IMAGE_SIZE
        val imageData = Arrays.copyOfRange(imageBytes, startBoundary, endBoundary)

        val imageDataList = imageData.iterator()
          .asSequence()
          .asStream().map { b -> b.toString() }
          .collect(Collectors.toList())
        imageDataList.add(label.toString())
        list.add(imageDataList)
    }
    return list
}

4.3. 人工ニューラルネットワークの構築

それでは、ニューラルネットワークを構築しましょう。 そのためには、次のものが必要です。

  • 複数の畳み込み層–これらの層は画像認識に最適であることが証明されています。 実際、ピクセルごとではなくゾーンで作業するという事実により、より良い形状認識が可能になります
  • プーリングレイヤーを畳み込みレイヤーと一緒に使用–プーリングレイヤーは、畳み込みレイヤーから受け取ったさまざまなプール値を単一のセルに集約するために使用されます
  • 複数のバッチ正規化レイヤーは、ニューロンの畳み込み層または単純なフィードフォワード層であるかどうかにかかわらず、さまざまな層の出力を正規化することによって過剰適合を回避します
  • 複数の通常のフィードフォワードニューロン層最後の畳み込み層の出力とニューラルネットワークモデルのブリッジ
private fun buildCNN(): MultiLayerNetwork {
    val multiLayerNetwork = MultiLayerNetwork(NeuralNetConfiguration.Builder()
      .seed(123)
      .l2(0.0005)
      .updater(Adam())
      .weightInit(WeightInit.XAVIER)
      .list()
      .layer(0, buildInitialConvolutionLayer())
      .layer(1, buildBatchNormalizationLayer())
      .layer(2, buildPoolingLayer())
      .layer(3, buildConvolutionLayer())
      .layer(4, buildBatchNormalizationLayer())
      .layer(5, buildPoolingLayer())
      .layer(6, buildDenseLayer())
      .layer(7, buildBatchNormalizationLayer())
      .layer(8, buildDenseLayer())
      .layer(9, buildOutputLayer())
      .setInputType(InputType.convolutionalFlat(28, 28, 1))
      .backprop(true)
      .build())
    multiLayerNetwork.init()
    return multiLayerNetwork
}

4.4. モデルのトレーニング

これで、モデルとデータセットの準備が整いました。 まだトレーニングルーチンが必要です。

private fun learning(cnn: MultiLayerNetwork, trainSet: RecordReaderDataSetIterator) {
    for (i in 0 until 10) {
        cnn.fit(trainSet)
    }
}

4.5. モデルのテスト

さらに、テストデータセットに対してモデルをテストするコードが必要です。

private fun testing(cnn: MultiLayerNetwork, testSet: RecordReaderDataSetIterator) {
    val evaluation = Evaluation(10)
    while (testSet.hasNext()) {
        val next = testSet.next()
        val output = cnn.output(next.features)
        evaluation.eval(next.labels, output)
    }
    println(evaluation.stats())
    println(evaluation.confusionToString())
}

4.6. 教師あり学習の実行

最終的に、これらすべてのメソッドを次々に呼び出して、モデルのパフォーマンスを確認できます。

val dataset = getDataSet()
dataset.shuffle()
val trainDatasetIterator = createDatasetIterator(dataset.subList(0, 50_000))
val testDatasetIterator = createDatasetIterator(dataset.subList(50_000, 60_000))

val cnn = buildCNN()
learning(cnn, trainDatasetIterator)
testing(cnn, testDatasetIterator)

4.7. 結果

最後に、数分のトレーニングの後、モデルがどのように機能しているかを確認できます。

==========================Scores========================================
 # of classes:    10
 Accuracy:        0,8609
 Precision:       0,8604
 Recall:          0,8623
 F1 Score:        0,8608
Precision, recall & F1: macro-averaged (equally weighted avg. of 10 classes)
========================================================================
   Predicted:         0      1      2      3      4      5      6      7      8      9
   Actual:
0  0          |     855      3     15     33      7      0     60      0      8      0
1  1          |       3    934      2     32      2      0      5      0      2      0
2  2          |      16      2    805      8     92      1     59      0      7      0
3  3          |      17     19      4    936     38      0     32      0      1      0
4  4          |       5      5     90     35    791      0    109      0      9      0
5  5          |       0      0      0      0      0    971      0     25      0     22
6  6          |     156      8    105     36     83      0    611      0     16      0
7  7          |       0      0      0      0      0     85      0    879      1     23
8  8          |       5      2      1      6      2      5      8      1    889      2
9  9          |       0      0      0      0      0     18      0     60      0    938

5. 結論

Kotlinを使用して機械学習モデルをトレーニングするために教師あり学習を使用する方法を見てきました。 Deeplearning4jを使用して、複雑なアルゴリズムを支援しました。 単純なアルゴリズムは、ライブラリがなくてもネイティブに実装できます。

いつものように、ソースコードはGitHubで入手できます。