Научитесь создавать, обучать и оценивать модель нелинейной регрессии для нескольких функций в браузере.

Машинное обучение, благодаря своим вычислительным возможностям, теперь является ключевым фактором, способствующим новому уровню технологических прорывов. Возможности и опыт интерактивного машинного обучения могут стать областью огромных масштабов в будущем. В результате в последние несколько лет машинное обучение стало развиваться и для Интернета.

В этой статье мы обсудим Tensorflow.js, браузерную JS-библиотеку с ускорением WebGL для обучения и развертывания моделей машинного обучения. . С помощью WebGL он использует возможности видеокарты в браузере, поскольку он может обрабатывать матричные операции аналогично графическому процессору.

Теперь, зачем нам ML в браузере?

- ML в браузере будет использовать преимущества взаимодействия с пользователем
- Будет использовать такие функции устройства, как камеры
- Он использует вычислительную мощность устройства, а не что-либо размещенное в облаке, и это делает машинное обучение в браузере более безопасным.

Что такое тензоры? Тензоры подобны матрицам, как многомерному массиву.

Тензор - это то, что имеет значения, размер, форму и тип данных.

Ниже представлена ​​документация по API для tensorflow.js, так как в этой статье мы не можем углубляться в возможности API-интерфейса tensorflow.js.

Https://js.tensorflow.org/api/latest/

Эта статья больше похожа на стартовую страницу для обычного JS-разработчика, которая может познакомиться с возможностями tensorflow.js в браузере.

Приступим,

Создание простой модели нелинейной регрессии для цены подержанного автомобиля Audi на основе заданного набора данных.

Набор данных был загружен с Kaggle, крупнейшего сообщества машинного обучения.

Ниже приводится подробная информация о наборе данных, который вы можете скачать по указанной ссылке. В этом решении мы выбрали audi.csv для создания и оценки модели.
https://www.kaggle.com/adityadesai13/used-car-dataset-ford-and-mercedes

Для модели ML нам нужно выбрать особенности входных тензоров и меток, выходных тензоров.

Прежде всего добавьте tensorflow.js через cdn или через npm, как указано ниже.

<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs/dist/tf.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-vis"></script>
//OR
npm i @tensorflow/tfjs

Анализируя данные, мы можем увидеть, что и пробег, и миль на галлон имеют некоторую связь с ценой автомобиля, поскольку оба обратно пропорциональны цене. Чтобы потреблять данные, нам нужно их обслуживать. Этого можно добиться, установив http-сервер и запустив http-сервер из папки, в которой сохранены данные.

npm i -g http-server

Давайте построим график зависимости характеристик от цены и посмотрим на корреляцию на графике.

// Import data
const audi_data = tf.data.csv(“http://127.0.0.1:8080/audi.csv");
// Extract x and y values to plot in graph
const dataSet = audi_data.map(record => ({
x: record.mileage,
y: record.price,
}));
const points = await dataSet.toArray();
plot(points, “Mileage”);

Теперь определите функцию построения графика как,

async function plot(pointsArray, featureName) {
tfvis.render.scatterplot(
{ name: `${featureName} vs Car Price` },
{ values: [pointsArray], series: [“original”] },
{
xLabel: featureName,
yLabel: “Price”,
}
)
}

Теперь давайте посмотрим, насколько эффективными могут быть обе функции и будет ли модель эффективно увеличиваться за счет объединения обеих функций.

Весь процесс можно разделить на 5 простых шагов

1. Извлечь элементы и метки
2. Нормализовать элементы и метки
3. Создать модель
4. Обучить модель
5. Оцените модель.

Случай 1: давайте оценим модель, используя пробег как функцию.

Как мы уже объяснили, как импортировать данные, мы можем сразу перейти к этапу извлечения признаков и меток. Прежде чем перейти к извлечению, нам нужно определить функцию нормализации. Это преобразует весь набор данных во что-то в диапазоне от 0 до 1, где 0 - самое низкое значение в наборе данных, а 1 - самое высокое значение. Также обратите внимание, что максимальное и минимальное значения, которые нам могут потребоваться для денормализации данных в будущем для прогноза, поскольку нам может потребоваться преобразовать данные обратно в их нормальное значение.

function normalise(tensor) {
const min = tensor.min();
const max = tensor.max();
const normalisedTensor = tensor.sub(min).div(max.sub(min));
return {
tensor: normalisedTensor,
min,
max
};
}

Извлеките и нормализуйте тензоры признаков и меток, как показано ниже:

//Shuffle
tf.util.shuffle(points);
// Extract Features (inputs)
const featureValues = points.map(p => p.x);
const featureTensor = tf.tensor2d(featureValues, [featureValues.length, 1]);
// Extract Labels (outputs)
const labelValues = points.map(p => p.y);
const labelTensor = tf.tensor2d(labelValues, [labelValues.length, 1]);
// Normalise features and labels
const normalisedFeature = normalise(featureTensor);
const normalisedLabel = normalise(labelTensor);

Чтобы оценить модель, нам нужно разделить функции и метки на наборы для обучения и тестирования, как показано ниже.

const [trainingFeatureTensor, testingFeatureTensor] = tf.split(normalisedFeature.tensor, 2);
const [trainingLabelTensor, testingLabelTensor] = tf.split(normalisedLabel.tensor, 2);

Теперь давайте создадим модель машинного обучения.
В tensorflow.js мы можем создавать собственные модели с помощью основных API для операций низкого уровня или использовать API слоев высокого уровня. В нашем примере мы используем Layers API Tensorflow.js для создания последовательной модели, как показано ниже.

function createModel() {
model = tf.sequential();
model.add(tf.layers.dense({
units: 1,
useBias: true,
activation: 'sigmoid',
inputDim: 1
}));
model.compile({
loss: 'meanSquaredError',
optimizer: tf.train.adam()
});
return model;
}

Здесь мы можем добавить столько плотных слоев и единиц, что может увеличить сложность модели и процесс обучения. Это также может замедлить процесс обучения, но может быть полезно для сложных наборов данных. Функция активации может быть линейной (линейная регрессия), сигмоидной (нелинейная регрессия) и т. Д. Для шаблонов кривых в наборе данных целесообразно использовать сигмоид с большим количеством слоев, поскольку это может обеспечить лучшее соответствие модели фактическому набору данных. После определения слоев нам нужно указать функцию потерь и оптимизатор, чтобы минимизировать потери.

Здесь мы добавили только один слой из 1 единицы. Обратите внимание на inputDim как 1, так как у нас есть только 1 функция в тензоре признаков.

В tensorflow.js доступны различные функции оптимизации, такие как Stochastic Gradient Descent, Adam и т. Д., В которых Adam является оптимизатором, не требующим скорости обучения. Он сам регулирует скорость обучения.

Теперь используйте функцию createModel, чтобы создать нашу модель в функции main.

//Create Model
const model = createModel();

Теперь модель поезда 4-го шага также может быть записана в отдельной функции, как показано ниже, и использоваться в нашей основной функции.

async function trainModel(model, trainingFeatureTensor, trainingLabelTensor) {
const { onBatchEnd, onEpochEnd } = tfvis.show.fitCallbacks({ name: "Training Performance" },['loss']);
return model.fit(trainingFeatureTensor, trainingLabelTensor, {batchSize: 32,
epochs: 20,
validationSplit: 0.2,
callbacks: {
onEpochEnd,
}});
}

Функция принимает модель, тензор признаков и тензор меток. Здесь мы указали 20% набора данных для процесса проверки, чтобы избежать переобучения.

Оптимизация происходит в конце каждого размера партии. Размер пакета - это параметр, который контролирует количество обучающих выборок, которые необходимо проработать перед обновлением внутренних параметров модели. Количество эпох - это параметр, который контролирует количество полных итераций для данного набора данных.

Теперь мы можем использовать функцию обучения для обучения набора данных, как показано ниже.

//Train Model
const result = await trainModel(model, trainingFeatureTensor, trainingLabelTensor);

Давайте посмотрим на потерю обучения и потерю проверки в консоли, добавив следующие строки.

const trainingLoss = result.history.loss.pop();
console.log(`Training loss: ${trainingLoss}`);
const validationLoss = result.history.val_loss.pop();
console.log(`Validation loss: ${validationLoss}`);

Наконец, мы можем оценить нашу модель с помощью

//Evaluate Model
const lossTensor = model.evaluate(testingFeatureTensor, testingLabelTensor);
const loss = await lossTensor.dataSync();
console.log(`Testing loss: ${loss}`);

И наша основная функция должна выглядеть примерно так, как показано ниже.

async function main() {
// Import data
const audi_data = tf.data.csv(“http://127.0.0.1:8080/audi.csv");
// Extract x and y values to plot in graph
const dataSet = audi_data.map(record => ({
x: record.mileage,
y: record.price
}));
const points = await dataSet.toArray();
//Shuffle
tf.util.shuffle(points);
// Extract Features (inputs)
const featureValues = points.map(p => p.x);
const featureTensor = tf.tensor2d(featureValues, [featureValues.length, 1]);
// Extract Labels (outputs)
const labelValues = points.map(p => p.y);
const labelTensor = tf.tensor2d(labelValues, [labelValues.length, 1]);
// Normalise features and labels
const normalisedFeature = normalise(featureTensor);
const normalisedLabel = normalise(labelTensor);
//Split testing and training tensors for features and labels
const [trainingFeatureTensor, testingFeatureTensor] = tf.split(normalisedFeature.tensor, 2);
const [trainingLabelTensor, testingLabelTensor] = tf.split(normalisedLabel.tensor, 2);
//Create Model
const model = createModel();
//Train Model
const result = await trainModel(model, trainingFeatureTensor, trainingLabelTensor);
const trainingLoss = result.history.loss.pop();
console.log(`Training loss: ${trainingLoss}`);
const validationLoss = result.history.val_loss.pop();
console.log(`Validation loss: ${validationLoss}`);
//Evaluate Model
const lossTensor = model.evaluate(testingFeatureTensor, testingLabelTensor);
const loss = await lossTensor.dataSync();
console.log(`Testing loss: ${loss}`);
}

Ниже приведены результаты обучения и тестирования модели на пробег.

Случай 2. Давайте оценим модель, используя миль на галлон в качестве функции.

Мы можем изменить функцию импорта данных, как показано ниже, и выполнить все остальные шаги или повторно использовать тот же код для миль на галлон в качестве функции.

// Import data
const audi_data = tf.data.csv(“http://127.0.0.1:8080/audi.csv");
// Extract x and y values to plot in graph
const dataSet = audi_data.map(record => ({
x: record.mpg,
y: record.price,
}));
const points = await dataSet.toArray();
plot(points, “MPG”);

Выполнив те же шаги, что и при оценке модели характеристик пробега, мы можем получить результаты, как показано ниже.

Случай 3. Давайте оценим модель, используя и пробег, и миль на галлон в качестве характеристик для модели прогнозирования цен. Ниже приводится обновленная основная функция для нескольких функций, поскольку нормализация должна выполняться для каждого тензора функций. Комбинирование обеих функций и нормализации приведет к неверным результатам прогноза.

const dataSet = audi_data.map((record) => ({
x1: record.mpg,
x2: record.mileage,
y: record.price
}));
const points = await dataSet.toArray();
//Shuffle
tf.util.shuffle(points);
// Extract and normalise features
const normalisedFeature1 = normalise(tf.tensor2d(points.map((p) => [p.x1]))).tensor.dataSync();
const normalisedFeature2 = normalise(tf.tensor2d(points.map((p) => [p.x2]))).tensor.dataSync();
const normFeatureInput = [];
normalisedFeature1.forEach((item, i, self) => {normFeatureInput.push([normalisedFeature1[i], normalisedFeature2[i]])})
// Extract and normalise Labels (outputs)
const labelValues = points.map(p => p.y);
const labelTensor = tf.tensor2d(labelValues, [labelValues.length, 1]);
const normalisedLabel = normalise(labelTensor);
//Training and testing data sets
const [trainingFeatureTensor, testingFeatureTensor] = tf.split(normFeatureInput, 2);
const [trainingLabelTensor, testingLabelTensor] = tf.split(normalisedLabel.tensor, 2);
//Create Model
const model = createModel();
//Train Model
const result = await trainModel(model, trainingFeatureTensor, trainingLabelTensor);
const trainingLoss = result.history.loss.pop();
console.log(`Training loss: ${trainingLoss}`);
const validationLoss = result.history.val_loss.pop();
console.log(`Validation loss: ${validationLoss}`);
//Evaluate Model
const lossTensor = model.evaluate(testingFeatureTensor, testingLabelTensor);
const loss = await lossTensor.dataSync();
console.log(`Testing loss: ${loss}`);

Следует отметить одну вещь: при создании модели для количества элементов n входной размер также должен быть n. В нашем случае inputDim должен быть 2. Перепишите функцию создания модели, как показано ниже

function createModel() {
model = tf.sequential();
model.add(tf.layers.dense({
units: 1,
useBias: true,
activation: 'sigmoid',
inputDim: 2,
}));
model.compile({
loss: 'meanSquaredError',
optimizer: tf.train.adam()
});
return model;
}

Ясно, что мы можем увидеть некоторое повышение эффективности, объединив две функции, а не одну. Также обратите внимание, что, используя два плотных слоя и 10 единиц во входном слое модели, как показано ниже, мы можем еще больше повысить эффективность модели. Обратите внимание, что единица выходного слоя должна соответствовать размеру метки. Поскольку у нас есть только 1 этикетка (цена), единицы второго уровня должны быть 1.

function createModel() {
model = tf.sequential();
model.add(tf.layers.dense({
units: 10,
useBias: true,
activation: 'sigmoid',
inputDim: 2,
}));
model.add(tf.layers.dense({
units: 1,
useBias: true,
activation: 'sigmoid',
}));
model.compile({
loss: 'meanSquaredError',
optimizer:tf.train.adam()
});
return model;
}

Ниже приведены потери при обучении, проверке и тестировании новой модели с двумя входными характеристиками (пробег и миль на галлон) и двумя уровнями.

Надеюсь, эта статья окажется полезной для новичков в tensorflow.js. Удачного кодирования :)