JavaRush /Java Blog /Random-JA /Java 開発者のための機械学習、パート 2

Java 開発者のための機械学習、パート 2

Random-JA グループに公開済み
Java 開発者のための機械学習、パート 1
Java 開発者のための機械学習、パート 2 - 1

目的関数の推定

予測関数としても知られるターゲット関数 は、準備またはトレーニング プロセスの結果であることを思い出してください。х数学的に言えば、変数を入力として受け取り、予測値を返す関数を見つけることが課題となりますу
Java 開発者のための機械学習、パート 2 - 2
機械学習では、コスト関数を(J(θ))使用して、特定の目的関数の誤差値または「コスト」を計算します。
Java 開発者のための機械学習、パート 2 - 3
コスト関数は、モデルがトレーニング データにどの程度適合しているかを示します。上に示した目的関数のコストを決定するには、各住宅例の二乗誤差を計算する必要があります(i)。誤差は、計算値と例からの家のу実際の値との間の距離です。 yi
Java 開発者のための機械学習、パート 2 - 4
たとえば、面積1330 の住宅の実質価格は 6,500,000 ユーロです。そして、トレーニングされた目的関数による予測住宅価格の差は7,032,478 ユーロ、差 (または誤差) は532,478 ユーロです。この違いは上のグラフでも確認できます。差 (または誤差) は、各価格領域トレーニング ペアの赤い垂直破線として表示されます。トレーニングされた目的関数のコストを計算したら、例の各住宅の二乗誤差を合計し、主値を計算する必要があります。価格値 が小さいほど(J(θ))、目的関数の予測はより正確になります。リスト3は、目的関数、トレーニング データのリスト、およびそれらに関連付けられたラベルを入力として受け取るコスト関数の単純な Java 実装を示しています。予測値はループで計算され、誤差は実際の価格値(ラベルから取得)を減算することによって計算されます。その後、誤差の 2 乗が合計され、誤差値が計算されます。コストは type の値として返されますdouble

リスト-3

public static double cost(Function<ltDouble[], Double> targetFunction,
 List<ltDouble[]> dataset,
 List<ltDouble> labels) {
 int m = dataset.size();
 double sumSquaredErrors = 0;

 // рассчет квадрата ошибки («разницы») для каждого тренировочного примера и //добавление его к сумме
 for (int i = 0; i < m; i++) {
 // получаем вектор признаков из текущего примера
 Double[] featureVector = dataset.get(i);
 // предсказываем meaning и вычисляем ошибку базируясь на реальном
 //значении (метка)
 double predicted = targetFunction.apply(featureVector);
 double label = labels.get(i);
 double gap = predicted - label;
 sumSquaredErrors += Math.pow(gap, 2);
 }

 // Вычисляем и возращаем meaning ошибки (чем меньше тем лучше)
 return (1.0 / (2 * m)) * sumSquaredErrors;
}
Java について興味がありますか? Java 開発者グループに参加してください。

ターゲット関数の学習

コスト関数は目的関数とシータ パラメーターの品質を評価するのに役立ちますが、最適なシータ パラメーターを見つける必要があります。これには勾配降下法アルゴリズムを使用できます。

勾配降下法

勾配降下法はコスト関数を最小化します。(J(θ))これは、トレーニング データに基づいて最小コストを持つシータ パラメーターを見つけるために使用されることを意味します。新しい、より適切なシータ値を計算するための簡略化されたアルゴリズムを次に示します。
Java 開発者のための機械学習、パート 2 - 5
したがって、シータ ベクトルのパラメータはアルゴリズムを繰り返すたびに改善されます。学習係数 α は、各反復における計算の数を指定します。これらの計算は、「適切な」シータ値が見つかるまで実行できます。たとえば、以下の線形回帰関数には 3 つのシータ パラメーターがあります。
Java 開発者のための機械学習、パート 2 - 6
反復ごとに、シータ パラメータ、 、およびのそれぞれに対して新しい値が計算されます。各反復の後、新しいシータ ベクトル0 , θ 1 , θ 2 }を使用して、より適切な新しい実装を作成できます。リスト-4 は、勾配減衰アルゴリズムの Java コードを示しています。回帰関数のシータは、トレーニング データ、マーカー データ、学習率を使用してトレーニングされます。その結果、シータ パラメーターを使用して目的関数が改善されます。このメソッドは何度も呼び出され、以前の計算からの新しい目的関数と新しいシータ パラメーターが渡されます。これらの呼び出しは、設定された目的関数が最小プラトーに達するまで繰り返されます。 θ0θ1θ2LinearRegressionFunction(α)train()

リスト-4

public static LinearRegressionFunction train(LinearRegressionFunction targetFunction,
 List<ltDouble[]> dataset,
 List<ltDouble> labels,
 double alpha) {
 int m = dataset.size();
 double[] thetaVector = targetFunction.getThetas();
 double[] newThetaVector = new double[thetaVector.length];

 // вычисление нового значения тета для каждого element тета массива
 for (int j = 0; j < thetaVector.length; j++) {
 // сумируем разницу ошибки * признак
 double sumErrors = 0;
 for (int i = 0; i < m; i++) {
 Double[] featureVector = dataset.get(i);
 double error = targetFunction.apply(featureVector) - labels.get(i);
 sumErrors += error * featureVector[j];
 }

 //вычисляем новые значения тета
 double gradient = (1.0 / m) * sumErrors;
 newThetaVector[j] = thetaVector[j] - alpha * gradient;
 }

 return new LinearRegressionFunction(newThetaVector);
}
コストを継続的に減少させるために、各トレーニング ステップの後にコスト関数を実行できますJ(θ)。各反復の後、コストは減少するはずです。これが起こらない場合は、学習係数の値が大きすぎて、アルゴリズムが単に最小値を逃していることを意味します。このような場合、勾配減衰アルゴリズムは失敗します。以下のプロットは、開始シータ ベクトル から始まる、新しく計算されたシータ パラメーターを使用した目的関数を示しています{1.0, 1.0}。左の列は、50 回の反復後の予測関数のプロットを示しています。200回繰り返した後の中央の列。右の列は 1000 回繰り返したものです。これらから、反復ごとに価格が減少し、新しい目的関数の適合性がますます向上していることがわかります。500 ~ 600 回の繰り返しの後、シータ パラメーターは大きく変化しなくなり、価格は安定したプラトーに達します。これ以降、この方法では目的関数の精度を向上させることはできません。
Java 開発者のための機械学習、パート 2 - 7
この場合、500 ~ 600 回の反復後にコストは大幅に減少しなくなりましたが、目的関数は依然として最適ではありません。これは不一致を示しています。機械学習では、「不一致」という用語は、学習アルゴリズムがデータの根本的な傾向を見つけられないことを意味するために使用されます。実際の経験に基づくと、大規模な物件では平方メートルあたりの価格の引き下げが期待される可能性があります。このことから、ターゲット関数の学習プロセスに使用されたモデルはデータに十分に適合していないと結論付けることができます。多くの場合、不一致はモデルの過度の単純化が原因で発生します。これは私たちの場合に起こりました。目的関数が単純すぎるため、分析には家の面積という単一のパラメーターが使用されます。しかし、この情報だけでは住宅価格を正確に予測するのに十分ではありません。

機能の追加とスケーリング

目的関数が解決しようとしている問題に対応していないことがわかった場合は、調整する必要があります。不一致を修正する一般的な方法は、特徴ベクトルに追加の特徴を追加することです。住宅の価格の例では、部屋の数や家の築年数などの特徴を追加できます。つまり、{size}家を表すために 1 つの特徴値を持つベクトルを使用する代わりに、複数の値を持つベクトルを使用できます。場合によっては{size, number-of-rooms, age}. 、利用可能なトレーニング データ内の特徴の数が十分ではないことがあります。その場合、既存の多項式特徴を使用して計算された多項式特徴を使用してみる価値があります。たとえば、住宅の価格を決定する目的関数を拡張して、平方メートル (x2) の計算された特徴を含めることができます。
Java 開発者のための機械学習、パート 2 - 8
複数の機能を使用するには、機能スケーリングが必要です。これは、さまざまな機能にわたる範囲を標準化するために使用されます。したがって、 size 2属性の値の範囲は、size 属性の値の範囲よりも大幅に大きくなります。特徴のスケーリングがなければ、サイズ2はコスト関数に過度の影響を与えます。size 2属性によって生じる誤差は、size 属性によって生じる誤差よりも大幅に大きくなります。簡単な特徴スケーリング アルゴリズムを以下に示します。
Java 開発者のための機械学習、パート 2 - 9
このアルゴリズムは、FeaturesScaling以下のコード例のクラスに実装されています。このクラスでは、FeaturesScalingトレーニング データに合わせて調整されたスケーリング関数を作成するための商用手法を紹介します。内部的には、トレーニング データ インスタンスを使用して、平均値、最小値、最大値が計算されます。結果の関数は特徴ベクトルを取得し、スケーリングされた特徴を含む新しいベクトルを生成します。以下に示すように、特徴スケーリングは学習プロセスと予測プロセスの両方に必要です。
// создание массива данных
List<ltDouble[]> dataset = new ArrayList<>();
dataset.add(new Double[] { 1.0, 90.0, 8100.0 }); // feature vector of house#1
dataset.add(new Double[] { 1.0, 101.0, 10201.0 }); // feature vector of house#2
dataset.add(new Double[] { 1.0, 103.0, 10609.0 }); // ...
//...

// создание меток
List<ltDouble> labels = new ArrayList<>();
labels.add(249.0); // price label of house#1
labels.add(338.0); // price label of house#2
labels.add(304.0); // ...
//...

// создание расширенного списка признаков
Function<ltDouble[], Double[]> scalingFunc = FeaturesScaling.createFunction(dataset);
List<ltDouble[]> scaledDataset = dataset.stream().map(scalingFunc).collect(Collectors.toList());

// создаем функцию которая инициализирует теты и осуществляет обучение //используя коэффициент обучения 0.1

LinearRegressionFunction targetFunction = new LinearRegressionFunction(new double[] { 1.0, 1.0, 1.0 });
for (int i = 0; i < 10000; i++) {
 targetFunction = Learner.train(targetFunction, scaledDataset, labels, 0.1);
}

// делаем предсказание стоимости дома с площадью 600 m2
Double[] scaledFeatureVector = scalingFunc.apply(new Double[] { 1.0, 600.0, 360000.0 });
double predictedPrice = targetFunction.apply(scaledFeatureVector);
特徴が追加されるにつれて、目的関数への適合度は高まりますが、注意が必要です。あまりにも多くの特徴を追加しすぎると、過剰適合した目的関数を学習することになる可能性があります。

過剰一致と相互検証

過学習は、目的関数またはモデルがトレーニング データに適合しすぎて、トレーニング データ内のノイズやランダムな変動が捕捉される場合に発生します。過学習の例を以下の右端のグラフに示します。
Java 開発者のための機械学習、パート 2 - 10
ただし、過学習モデルはトレーニング データに対しては非常に優れたパフォーマンスを発揮しますが、実際の未知のデータに対してはパフォーマンスが低下します。過剰適合を避ける方法はいくつかあります。
  • トレーニングにはより大きなデータセットを使用します。
  • 上のグラフに示すように、使用する機能を減らします。
  • 正則化を考慮した改良された機械学習アルゴリズムを使用します。
予測アルゴリズムがトレーニング データに過剰適合する場合は、その精度に利益をもたらさない特徴を削除する必要があります。難しいのは、他の特徴よりも予測の精度に大きな影響を与える特徴を見つけることです。グラフに示されているように、オーバーフィットはグラフを使用して視覚的に判断できます。これは 2 つまたは 3 つの座標を持つグラフに適していますが、3 つ以上の特徴を使用すると、グラフのプロットと評価が困難になります。相互検証では、トレーニング プロセスが完了した後、アルゴリズムにとって未知のデータを使用してトレーニング後にモデルを再テストします。利用可能なラベル付きデータは 3 つのセットに分割する必要があります。
  • トレーニングデータ。
  • 検証データ。
  • テストデータ。
この場合、家を特徴づけるラベル付きレコードの 60% を、ターゲット アルゴリズムのバリアントのトレーニングのプロセスで使用する必要があります。トレーニング プロセスの後、残りのデータ (以前に使用されていない) の半分を使用して、トレーニングされたターゲット アルゴリズムが未知のデータに対して適切に実行されることを検証する必要があります。通常、他のアルゴリズムよりもパフォーマンスの良いアルゴリズムが選択されて使用されます。残りのデータは、最終的に選択されたモデルの誤差値を計算するために使用されます。k-foldなど、他の交差検証手法もあります。ただし、この記事ではそれらについては説明しません。

機械学習ツールと Weka フレームワーク

ほとんどのフレームワークとライブラリは、機械学習アルゴリズムの広範なコレクションを提供します。さらに、データ モデルのトレーニング、テスト、処理に便利な高レベルのインターフェイスを提供します。Weka は、JVM で最も人気のあるフレームワークの 1 つです。Weka は、モデルを検証するためのグラフィカル テストを含む実用的な Java ライブラリです。以下の例では、Weka ライブラリを使用して、フィーチャとラベルを含むトレーニング データセットを作成します。方法setClassIndex()- マーキング用。Weka では、ラベルはクラスとして定義されます。
// определяем атрибуты для признаков и меток
ArrayList<ltAttribute> attributes = new ArrayList<>();
Attribute sizeAttribute = new Attribute("sizeFeature");
attributes.add(sizeAttribute);
Attribute squaredSizeAttribute = new Attribute("squaredSizeFeature");
attributes.add(squaredSizeAttribute);
Attribute priceAttribute = new Attribute("priceLabel");
attributes.add(priceAttribute);


// создаем и заполняем список признаков 5000 примеров
Instances trainingDataset = new Instances("trainData", attributes, 5000);
trainingDataset.setClassIndex(trainingSet.numAttributes() - 1);
Instance instance = new DenseInstance(3);

instance.setValue(sizeAttribute, 90.0);
instance.setValue(squaredSizeAttribute, Math.pow(90.0, 2));
instance.setValue(priceAttribute, 249.0);
trainingDataset.add(instance);
Instance instance = new DenseInstance(3);
instance.setValue(sizeAttribute, 101.0);
...
データ セットとサンプル オブジェクトは、ファイルに保存したり、ファイルからロードしたりできます。Weka は、 Weka のグラフィックス ベンチマークでサポートされているARFF (属性関係ファイル形式) を使用します。このデータセットは、Weka で分類子として知られる目的関数をトレーニングするために使用されます。まず最初に、目的関数を定義する必要があります。以下のコードはLinearRegression分類子のインスタンスを作成します。この分類器は、 を使用してトレーニングされますbuildClassifier()。この方法では、buildClassifier()トレーニング データに基づいてシータ パラメーターを選択し、最適なターゲット モデルを検索します。Weka を使用すると、学習率や反復回数の設定について心配する必要はありません。Weka は機能スケーリングも独​​立して実行します。
Classifier targetFunction = new LinearRegression();
targetFunction.buildClassifier(trainingDataset);
これらの設定を行うと、以下に示すように、目的関数を使用して住宅の価格を予測できます。
Instances unlabeledInstances = new Instances("predictionset", attributes, 1);
unlabeledInstances.setClassIndex(trainingSet.numAttributes() - 1);
Instance unlabeled = new DenseInstance(3);
unlabeled.setValue(sizeAttribute, 1330.0);
unlabeled.setValue(squaredSizeAttribute, Math.pow(1330.0, 2));
unlabeledInstances.add(unlabeled);

double prediction = targetFunction.classifyInstance(unlabeledInstances.get(0));
EvaluationWeka は、トレーニングされた分類子またはモデルをテストするための クラスを提供します。以下のコードでは、誤った結果を避けるために、検証データの選択された配列が使用されています。測定結果(誤差コスト)がコンソールに表示されます。通常、評価結果は、さまざまな機械学習アルゴリズム、またはそのバリエーションを使用してトレーニングされたモデルを比較するために使用されます。
Evaluation evaluation = new Evaluation(trainingDataset);
evaluation.evaluateModel(targetFunction, validationDataset);
System.out.println(evaluation.toSummaryString("Results", false));
上の例では線形回帰を使用し、入力値に基づいて家の価格などの数値を予測します。線形回帰は、連続数値の予測をサポートします。バイナリ値 (「はい」と「いいえ」) を予測するには、他の機械学習アルゴリズムを使用する必要があります。たとえば、決定木、ニューラル ネットワーク、ロジスティック回帰などです。
// использование логистической регрессии
Classifier targetFunction = new Logistic();
targetFunction.buildClassifier(trainingSet);
これらのアルゴリズムのいずれかを使用すると、たとえば、電子メール メッセージがスパムかどうかを予測したり、天気を予測したり、家がよく売れるかどうかを予測したりできます。天気や家がどのくらい早く売れるかを予測するようにアルゴリズムを教えたい場合は、別のデータセットが必要です。topseller:
// использование атрибута маркера topseller instead of атрибута маркера цена
ArrayList<string> classVal = new ArrayList<>();
classVal.add("true");
classVal.add("false");

Attribute topsellerAttribute = new Attribute("topsellerLabel", classVal);
attributes.add(topsellerAttribute);
このデータセットは、新しい分類器をトレーニングするために使用されますtopseller。トレーニングが完了すると、予測呼び出しは、予測値を取得するために使用できるトークン クラス インデックスを返す必要があります。
int idx = (int) targetFunction.classifyInstance(unlabeledInstances.get(0));
String prediction = classVal.get(idx);

結論

機械学習は統計と密接に関連しており、多くの数学的概念を使用しますが、機械学習ツールキットを使用すると、数学の深い知識がなくても機械学習をプログラムに統合し始めることができます。ただし、この記事で説明した線形回帰アルゴリズムなど、基礎となる機械学習アルゴリズムを理解すればするほど、適切なアルゴリズムを選択し、最適なパフォーマンスが得られるように調整できるようになります。 英語からの翻訳。著者: Gregor Roth、JavaWorld ソフトウェア アーキテクト。
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION