I. 多階級化問題の紹介
これまでのノートでは、主に2値分類のロジスティック回帰問題について学びました。しかし、実生活では、2値分類問題だけでなく、多値分類問題も含まれます。例えば、紙に数字を書き、それが0~9のどの数字であるかを予測したい場合、この問題は0~9の10分類からなる多分類問題です。別の例としては、猫、犬、豚、鶏、アヒル、ガチョウなど、何千もの分類がある写真を分類することです。このような多分類の課題に直面した場合、どのようなニューラルネットワークを構築すればよいのでしょうか?
まず、古典的なアイリスの分類問題を見てみましょう:
アヤメの花は、花弁の長さと幅、萼の長さと幅から、Variegated Iris、Mountain Iris、Virginia Irisの3つに分類されます。入力はイメージではなく、花弁の長さと幅、萼片の長さと幅からなる数値の束、つまり合計4つの特徴です。
操作手順:
1) 虹彩データセットを読み込みます;
いわゆる検証セットは、簡単に言えば、モデルが本当に訓練が多ければ多いほど良いかどうかを検証するために使用され、実際には、検証セットは、多くの場合、訓練セットの一部の訓練セットから分割され、それは訓練セットの形式と同じです。
2) モデル構造の定義:活性化関数softmaxを持つ多層ニューラルネットワーク;
活性化関数ソフトマックスの役割:ここでは3つの分類があるので、3つの分類の確率を求めます。ソフトマックスの役割は、これらの3つの確率を1つの確率にまとめることです。
3) モデルを訓練し、予測します。ここでは、使用する損失などに加えて、精度という新しい単位を追加します;
II.IRISデータセットの読み込み
事前に用意されたスクリプトを使用して、トレーニングセットと検証セットを含むIRISデータセットを生成します。
src/iris/data.js
/**
* @license
* Copyright 2018 Google LLC. All Rights Reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://..//-.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* =============================================================================
*/
import * as tf from '@tensorflow/tfjs';
export const IRIS_CLASSES = [' ', '可変カラーアイリス', 'バージニア虹彩'];
export const IRIS_NUM_CLASSES = IRIS_CLASSES.length;
// Iris flowers data. Source:
// https://...//--//.ta
// 下のデータの各項目の最初の4つの数字が特徴で、最後の数字がカテゴリーである:0-マウンテン・アイリス1-可変カラーアイリス、2-バージニア虹彩
const IRIS_DATA = [
[5.1, 3.5, 1.4, 0.2, 0],
[4.9, 3.0, 1.4, 0.2, 0],
[4.7, 3.2, 1.3, 0.2, 0],
[4.6, 3.1, 1.5, 0.2, 0],
[5.0, 3.6, 1.4, 0.2, 0],
[5.4, 3.9, 1.7, 0.4, 0],
[4.6, 3.4, 1.4, 0.3, 0],
[5.0, 3.4, 1.5, 0.2, 0],
[4.4, 2.9, 1.4, 0.2, 0],
[4.9, 3.1, 1.5, 0.1, 0],
[5.4, 3.7, 1.5, 0.2, 0],
[4.8, 3.4, 1.6, 0.2, 0],
[4.8, 3.0, 1.4, 0.1, 0],
[4.3, 3.0, 1.1, 0.1, 0],
[5.8, 4.0, 1.2, 0.2, 0],
[5.7, 4.4, 1.5, 0.4, 0],
[5.4, 3.9, 1.3, 0.4, 0],
[5.1, 3.5, 1.4, 0.3, 0],
[5.7, 3.8, 1.7, 0.3, 0],
[5.1, 3.8, 1.5, 0.3, 0],
[5.4, 3.4, 1.7, 0.2, 0],
[5.1, 3.7, 1.5, 0.4, 0],
[4.6, 3.6, 1.0, 0.2, 0],
[5.1, 3.3, 1.7, 0.5, 0],
[4.8, 3.4, 1.9, 0.2, 0],
[5.0, 3.0, 1.6, 0.2, 0],
[5.0, 3.4, 1.6, 0.4, 0],
[5.2, 3.5, 1.5, 0.2, 0],
[5.2, 3.4, 1.4, 0.2, 0],
[4.7, 3.2, 1.6, 0.2, 0],
[4.8, 3.1, 1.6, 0.2, 0],
[5.4, 3.4, 1.5, 0.4, 0],
[5.2, 4.1, 1.5, 0.1, 0],
[5.5, 4.2, 1.4, 0.2, 0],
[4.9, 3.1, 1.5, 0.1, 0],
[5.0, 3.2, 1.2, 0.2, 0],
[5.5, 3.5, 1.3, 0.2, 0],
[4.9, 3.1, 1.5, 0.1, 0],
[4.4, 3.0, 1.3, 0.2, 0],
[5.1, 3.4, 1.5, 0.2, 0],
[5.0, 3.5, 1.3, 0.3, 0],
[4.5, 2.3, 1.3, 0.3, 0],
[4.4, 3.2, 1.3, 0.2, 0],
[5.0, 3.5, 1.6, 0.6, 0],
[5.1, 3.8, 1.9, 0.4, 0],
[4.8, 3.0, 1.4, 0.3, 0],
[5.1, 3.8, 1.6, 0.2, 0],
[4.6, 3.2, 1.4, 0.2, 0],
[5.3, 3.7, 1.5, 0.2, 0],
[5.0, 3.3, 1.4, 0.2, 0],
[7.0, 3.2, 4.7, 1.4, 1],
[6.4, 3.2, 4.5, 1.5, 1],
[6.9, 3.1, 4.9, 1.5, 1],
[5.5, 2.3, 4.0, 1.3, 1],
[6.5, 2.8, 4.6, 1.5, 1],
[5.7, 2.8, 4.5, 1.3, 1],
[6.3, 3.3, 4.7, 1.6, 1],
[4.9, 2.4, 3.3, 1.0, 1],
[6.6, 2.9, 4.6, 1.3, 1],
[5.2, 2.7, 3.9, 1.4, 1],
[5.0, 2.0, 3.5, 1.0, 1],
[5.9, 3.0, 4.2, 1.5, 1],
[6.0, 2.2, 4.0, 1.0, 1],
[6.1, 2.9, 4.7, 1.4, 1],
[5.6, 2.9, 3.6, 1.3, 1],
[6.7, 3.1, 4.4, 1.4, 1],
[5.6, 3.0, 4.5, 1.5, 1],
[5.8, 2.7, 4.1, 1.0, 1],
[6.2, 2.2, 4.5, 1.5, 1],
[5.6, 2.5, 3.9, 1.1, 1],
[5.9, 3.2, 4.8, 1.8, 1],
[6.1, 2.8, 4.0, 1.3, 1],
[6.3, 2.5, 4.9, 1.5, 1],
[6.1, 2.8, 4.7, 1.2, 1],
[6.4, 2.9, 4.3, 1.3, 1],
[6.6, 3.0, 4.4, 1.4, 1],
[6.8, 2.8, 4.8, 1.4, 1],
[6.7, 3.0, 5.0, 1.7, 1],
[6.0, 2.9, 4.5, 1.5, 1],
[5.7, 2.6, 3.5, 1.0, 1],
[5.5, 2.4, 3.8, 1.1, 1],
[5.5, 2.4, 3.7, 1.0, 1],
[5.8, 2.7, 3.9, 1.2, 1],
[6.0, 2.7, 5.1, 1.6, 1],
[5.4, 3.0, 4.5, 1.5, 1],
[6.0, 3.4, 4.5, 1.6, 1],
[6.7, 3.1, 4.7, 1.5, 1],
[6.3, 2.3, 4.4, 1.3, 1],
[5.6, 3.0, 4.1, 1.3, 1],
[5.5, 2.5, 4.0, 1.3, 1],
[5.5, 2.6, 4.4, 1.2, 1],
[6.1, 3.0, 4.6, 1.4, 1],
[5.8, 2.6, 4.0, 1.2, 1],
[5.0, 2.3, 3.3, 1.0, 1],
[5.6, 2.7, 4.2, 1.3, 1],
[5.7, 3.0, 4.2, 1.2, 1],
[5.7, 2.9, 4.2, 1.3, 1],
[6.2, 2.9, 4.3, 1.3, 1],
[5.1, 2.5, 3.0, 1.1, 1],
[5.7, 2.8, 4.1, 1.3, 1],
[6.3, 3.3, 6.0, 2.5, 2],
[5.8, 2.7, 5.1, 1.9, 2],
[7.1, 3.0, 5.9, 2.1, 2],
[6.3, 2.9, 5.6, 1.8, 2],
[6.5, 3.0, 5.8, 2.2, 2],
[7.6, 3.0, 6.6, 2.1, 2],
[4.9, 2.5, 4.5, 1.7, 2],
[7.3, 2.9, 6.3, 1.8, 2],
[6.7, 2.5, 5.8, 1.8, 2],
[7.2, 3.6, 6.1, 2.5, 2],
[6.5, 3.2, 5.1, 2.0, 2],
[6.4, 2.7, 5.3, 1.9, 2],
[6.8, 3.0, 5.5, 2.1, 2],
[5.7, 2.5, 5.0, 2.0, 2],
[5.8, 2.8, 5.1, 2.4, 2],
[6.4, 3.2, 5.3, 2.3, 2],
[6.5, 3.0, 5.5, 1.8, 2],
[7.7, 3.8, 6.7, 2.2, 2],
[7.7, 2.6, 6.9, 2.3, 2],
[6.0, 2.2, 5.0, 1.5, 2],
[6.9, 3.2, 5.7, 2.3, 2],
[5.6, 2.8, 4.9, 2.0, 2],
[7.7, 2.8, 6.7, 2.0, 2],
[6.3, 2.7, 4.9, 1.8, 2],
[6.7, 3.3, 5.7, 2.1, 2],
[7.2, 3.2, 6.0, 1.8, 2],
[6.2, 2.8, 4.8, 1.8, 2],
[6.1, 3.0, 4.9, 1.8, 2],
[6.4, 2.8, 5.6, 2.1, 2],
[7.2, 3.0, 5.8, 1.6, 2],
[7.4, 2.8, 6.1, 1.9, 2],
[7.9, 3.8, 6.4, 2.0, 2],
[6.4, 2.8, 5.6, 2.2, 2],
[6.3, 2.8, 5.1, 1.5, 2],
[6.1, 2.6, 5.6, 1.4, 2],
[7.7, 3.0, 6.1, 2.3, 2],
[6.3, 3.4, 5.6, 2.4, 2],
[6.4, 3.1, 5.5, 1.8, 2],
[6.0, 3.0, 4.8, 1.8, 2],
[6.9, 3.1, 5.4, 2.1, 2],
[6.7, 3.1, 5.6, 2.4, 2],
[6.9, 3.1, 5.1, 2.3, 2],
[5.8, 2.7, 5.1, 1.9, 2],
[6.8, 3.2, 5.9, 2.3, 2],
[6.7, 3.3, 5.7, 2.5, 2],
[6.7, 3.0, 5.2, 2.3, 2],
[6.3, 2.5, 5.0, 1.9, 2],
[6.5, 3.0, 5.2, 2.0, 2],
[6.2, 3.4, 5.4, 2.3, 2],
[5.9, 3.0, 5.1, 1.8, 2],
];
/**
* Convert Iris data arrays to `tf.Tensor`s.
*
* @param data The Iris input feature data, an `Array` of `Array`s, each element
* of which is assumed to be a length-4 `Array` (for petal length, petal
* width, sepal length, sepal width).
* @param targets An `Array` of numbers, with values from the set {0, 1, 2}:
* representing the true category of the Iris flower. Assumed to have the same
* array length as `data`.
* @param testSplit Fraction of the data at the end to split as test data: a
* number between 0 and 1.
* @return A length-4 `Array`, with
* - training data as `tf.Tensor` of shape [numTrainExapmles, 4].
* - training one-hot labels as a `tf.Tensor` of shape [numTrainExamples, 3]
* - test data as `tf.Tensor` of shape [numTestExamples, 4].
* - test one-hot labels as a `tf.Tensor` of shape [numTestExamples, 3]
*/
function convertToTensors(data, targets, testSplit) {
const numExamples = data.length;
if (numExamples !== targets.length) {
throw new Error('data and split have different numbers of examples');
}
// Randomly shuffle `data` and `targets`.
const indices = [];
for (let i = 0; i < numExamples; ++i) {
indices.push(i);
}
tf.util.shuffle(indices);
const shuffledData = [];
const shuffledTargets = [];
for (let i = 0; i < numExamples; ++i) {
shuffledData.push(data[indices[i]]);
shuffledTargets.push(targets[indices[i]]);
}
// Split the data into a training set and a tet set, based on `testSplit`.
const numTestExamples = Math.round(numExamples * testSplit);
const numTrainExamples = numExamples - numTestExamples;
const xDims = shuffledData[0].length;
// Create a 2D `tf.Tensor` to hold the feature data.
const xs = tf.tensor2d(shuffledData, [numExamples, xDims]);
// Create a 1D `tf.Tensor` to hold the labels, and convert the number label
// from the set {0, 1, 2} into one-hot encoding (.e.g., 0 --> [1, 0, 0]).
const ys = tf.oneHot(tf.tensor1d(shuffledTargets).toInt(), IRIS_NUM_CLASSES);
// Split the data into training and test sets, using `slice`.
const xTrain = xs.slice([0, 0], [numTrainExamples, xDims]);
const xTest = xs.slice([numTrainExamples, 0], [numTestExamples, xDims]);
const yTrain = ys.slice([0, 0], [numTrainExamples, IRIS_NUM_CLASSES]);
const yTest = ys.slice([0, 0], [numTestExamples, IRIS_NUM_CLASSES]);
return [xTrain, yTrain, xTest, yTest];
}
/**
* Obtains Iris data, split into training and test sets.
*
* @param testSplit Fraction of the data at the end to split as test data: a
* number between 0 and 1.
*
* @param return A length-4 `Array`, with
* - training data as an `Array` of length-4 `Array` of numbers.
* - training labels as an `Array` of numbers, with the same length as the
* return training data above. Each element of the `Array` is from the set
* {0, 1, 2}.
* - test data as an `Array` of length-4 `Array` of numbers.
* - test labels as an `Array` of numbers, with the same length as the
* return test data above. Each element of the `Array` is from the set
* {0, 1, 2}.
*/
export function getIrisData(testSplit) {
return tf.tidy(() => {
const dataByClass = [];
const targetsByClass = [];
for (let i = 0; i < IRIS_CLASSES.length; ++i) {
dataByClass.push([]);
targetsByClass.push([]);
}
for (const example of IRIS_DATA) {
const target = example[example.length - 1];
const data = example.slice(0, example.length - 1);
dataByClass[target].push(data);
targetsByClass[target].push(target);
}
const xTrains = [];
const yTrains = [];
const xTests = [];
const yTests = [];
for (let i = 0; i < IRIS_CLASSES.length; ++i) {
const [xTrain, yTrain, xTest, yTest] =
convertToTensors(dataByClass[i], targetsByClass[i], testSplit);
xTrains.push(xTrain);
yTrains.push(yTrain);
xTests.push(xTest);
yTests.push(yTest);
}
const concatAxis = 0;
return [
tf.concat(xTrains, concatAxis), tf.concat(yTrains, concatAxis),
tf.concat(xTests, concatAxis), tf.concat(yTests, concatAxis)
];
});
}
IRISデータセットの印刷
今回は、4つの入力特徴に1つの出力カテゴリーを加えた合計5次元のデータを可視化するのは難しいため、このデータセットは再度可視化しません。
まずデータを持ち込んで印刷し、結果のデータ形式がどのようになるかを確認します:
src/iris/index.js
import {
getIrisData,
IRIS_CLASSES
} from './data';
window.onload = () => {
const [xTrain, yTrain, xTest, yTest] = getIrisData(0.15); // ここでパラメータ0.15データセットから分割された表現15%xTrainは訓練セットの全ての特徴量、yTrainは訓練セットの全てのラベル、xTestは訓練セットの全ての特徴量、yTestは検証セットの全てのラベルである。
xTrain.print();
yTrain.print();
xTest.print();
yTest.print();
console.log(IRIS_CLASSES);
}
III.モデル構造の定義:ソフトマックスによる多層ニューラルネットワーク
理論です:
上記のIRISデータセットには4つの特徴があり、明らかに複雑な非線形問題です。このような問題を解決するためには、多層ニューラルネットワークが必要であり、非線形変化をもたらす活性化関数と結合する必要があります。IRISデータセットでは、多階層分類問題も解決しなければなりません。解決方法は?ニューラルネットワークの最終層にソフトマックス活性化関数を設定します。二値分類問題を解く場合、ニューラルネットワークの最終層にはシグモイド活性化関数が使用され、0から1までの確率を出力します。しかし、多分類の問題を解くときは、1つの確率では不十分です。ここではそれぞれの分類の確率を出力する必要があり、ソフトマックス活性化関数がこれを可能にします。ソフトマックス活性化関数を設定した後、ニューロンの数をニューラルネットワークの最終層の分類数に設定する必要があります。
操作手順:
ニューラルネットワークの初期化 ニューラルネットワークに2つの層を追加 ニューロンの数、inputShape、層の活性化関数を設計します。
コードは以下の通り:
src/iris/index.js
import * as tfjs from '@tensorflow/tfjs';
import {
getIrisData,
IRIS_CLASSES
} from './data';
window.onload = () => {
const [xTrain, yTrain, xTest, yTest] = getIrisData(0.15); // ここでパラメータ0.15データセットから分割された表現15%xTrainは訓練セットの全ての特徴量、yTrainは訓練セットの全てのラベル、xTestは訓練セットの全ての特徴量、yTestは検証セットの全てのラベルである。
console.log(xTrain.shape); // [126,4]トレーニングセットには126個のデータがあることを示している。
console.log(xTest.shape); // [24,4]検証セットには24個のデータがあることを示している。
xTrain.print();
yTrain.print();
xTest.print();
yTest.print();
console.log(IRIS_CLASSES);
// モデルの初期化
const model = tf.sequential();
// 最初のレイヤーを追加する
model.add(tf.layers.dense({
units: 10,
inputShape: [xTrain.shape[1]],
activation: 'sigmoid', // この第1層の活性化関数は、入力と出力に非線形な変化をもたらすだけでよい。
}));
// 第2層を追加する
model.add(tf.layers.dense({
units: 3,
activation: 'softmax'
}));
}
IV. 学習モデル:クロスエントロピー損失関数と精度指標
クロスエントロピー損失関数は、学習された対数損失関数の多範疇バージョンです。
操作手順:
1) クロスエントロピー損失関数の設定
2) 精度測定の追加
3) tfvisを用いたモデルの学習と学習過程の可視化
クロスエントロピー損失とは何ですか?
ml-cheatsheet.readthedocs.io/ja/latest/l...
クロスエントロピー損失または対数損失は、0から1の間の確率値を出力とする分類モデルの性能を測定します。予測された確率が実際のラベルから乖離するにつれて、クロスエントロピー損失は増加します。
カテゴリ数Mが2の2値分類では、クロス・エントロピー損失は対数損失であり、クロス・エントロピーは次のように計算できます:
-+log(1-p))
M > 2の場合、各カテゴリ・ラベルの各オブザベーションについて個別の損失を計算し、結果を合計します。
その中でも
M - カテゴリーの数
log - 自然対数
y-カテゴリの真値
p-カテゴリの予測確率
コードは以下の通り:
src/iris/index.js
import * as tf from '@tensorflow/tfjs';
import * as tfvis from '@tensorflow/tfjs-vis';
import {
getIrisData,
IRIS_CLASSES
} from './data';
import { callbacks } from '@tensorflow/tfjs';
window.onload = async () => {
const [xTrain, yTrain, xTest, yTest] = getIrisData(0.15); // ここでパラメータ0.15データセットから分割された表現15%xTrainは訓練セットの全ての特徴量、yTrainは訓練セットの全てのラベル、xTestは訓練セットの全ての特徴量、yTestは検証セットの全てのラベルである。
console.log(xTrain.shape); // [126,4]トレーニングセットには126個のデータがあることを示している。
console.log(xTest.shape); // [24,4]検証セットには24個のデータがあることを示している。
xTrain.print();
yTrain.print();
xTest.print();
yTest.print();
console.log(IRIS_CLASSES);
// モデルの初期化
const model = tf.sequential();
// 最初のレイヤーを追加する
model.add(tf.layers.dense({
units: 10,
inputShape: [xTrain.shape[1]],
activation: 'sigmoid', // この第1層の活性化関数は、入力と出力に非線形な変化をもたらすだけでよい。
}));
// 第2層を追加する
model.add(tf.layers.dense({
units: 3,
activation: 'softmax'
}));
model.compile({
loss: 'categoricalCrossentropy', // クロスエントロピー損失関数
optimizer: tf.train.adam(0.1), // adam
metrics: ['accuracy'] // これが精度の指標である。
});
// 学習モデル
await model.fit(xTrain, yTrain, {
epochs: 100,
validationData: [xTest, yTest], //
callbacks: tfvis.show.fitCallbacks( // 学習プロセスを視覚化する
{ name: 'トレーニング効果' },
[
'loss', // トレーニングセットの損失
'val_loss', // 検証セットの損失
'acc', // トレーニングセットの精度
'val_acc' // 検証セットの精度
],
{ callbacks: ['onEpochEnd'] } // onEpochEndのみが表示され、onEpochEndは表示されないように設定されている。onBatchEnd
)
})
}
その効果は以下の通り:
図の上部はトレーニングセットでの損失と検証セットでの損失を、図の下部はトレーニングセットでの精度と検証セットでの精度を示しています。
V. 予測の作成
操作手順:
1) 予測するデータを入力するためのフロントエンドインターフェイスの準備
2) 学習済みモデルによる予測
3) 出力されたテンソルを通常のデータに変換して表示
コードは次のとおりです: src/iris/data.js
import * as tf from '@tensorflow/tfjs';
import * as tfvis from '@tensorflow/tfjs-vis';
import {
getIrisData,
IRIS_CLASSES
} from './data';
import { callbacks } from '@tensorflow/tfjs';
window.onload = async () => {
const [xTrain, yTrain, xTest, yTest] = getIrisData(0.15); // ここでパラメータ0.15データセットから分割された表現15%xTrainは訓練セットの全ての特徴量、yTrainは訓練セットの全てのラベル、xTestは訓練セットの全ての特徴量、yTestは検証セットの全てのラベルである。
console.log(xTrain.shape); // [126,4]トレーニングセットには126個のデータがあることを示している。
console.log(xTest.shape); // [24,4]検証セットには24個のデータがあることを示している。
xTrain.print();
yTrain.print();
xTest.print();
yTest.print();
console.log(IRIS_CLASSES);
// モデルの初期化
const model = tf.sequential();
// 最初のレイヤーを追加する
model.add(tf.layers.dense({
units: 10,
inputShape: [xTrain.shape[1]],
activation: 'sigmoid', // この第1層の活性化関数は、入力と出力に非線形な変化をもたらすだけでよい。
}));
// 第2層を追加する
model.add(tf.layers.dense({
units: 3,
activation: 'softmax'
}));
model.compile({
loss: 'categoricalCrossentropy', // クロスエントロピー損失関数
optimizer: tf.train.adam(0.1), // adam
metrics: ['accuracy'] // これが精度の指標である。
});
// 学習モデル
await model.fit(xTrain, yTrain, {
epochs: 100,
validationData: [xTest, yTest], //
callbacks: tfvis.show.fitCallbacks( // 学習プロセスを視覚化する
{ name: 'トレーニング効果' },
[
'loss', // トレーニングセットの損失
'val_loss', // 検証セットの損失
'acc', // トレーニングセットの精度
'val_acc' // 検証セットの精度
],
{ callbacks: ['onEpochEnd'] } // onEpochEndのみが表示され、onEpochEndは表示されないように設定されている。onBatchEnd
)
})
window.predict = (form) => {
const input = tf.tensor([[
+form.a.value,
+form.b.value,
+form.c.value,
+form.d.value,
]]); // 入力データと学習データのフォーマットは同じでなければならない。
const pred = model.predict(input);
alert(`予測結果${IRIS_CLASSES[pred.argMax(1).dataSync(0)]}`); // pred.argMax(1)出力はpredの2次元目の最大値の座標だが、Tensorを出力するので、dataSyncで通常のデータに変換して表示する必要がある。
}
}
src/iris/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="index.js"></script>
<form action="" onsubmit="predict(this); return false;">
x: <input type="text" name="x">
y: <input type="text" name="y">
<button type="submit"> </button>
</form>
</body>
</html>
次に、学習データからランダムなデータをテストします。入力と出力を図に示します:





