Sytuacja kobiet w IT w 2024 roku
4.09.20197 min
Pier Paolo Ippolito

Pier Paolo IppolitoTop Writer in Artificial Intelligence Towards Data Science

Uczenie maszynowe online z użyciem Tensorflow.js

Naucz się tworzyć, trenować i testować model uczenia maszynowego w przeglądarce za pomocą Tensorflow.js.

Uczenie maszynowe online z użyciem Tensorflow.js

Dzięki najnowszym osiągnięciom sztucznej inteligencji, budowanie i trenowanie modeli uczenia maszynowego jest obecnie stosunkowo łatwe. Chociaż modele te mogą przynieść korzyści społeczeństwu jedynie poprzez dzielenie się nimi, by mogły zostać zastosowane online.

Wyjaśnię dziś, jak stworzyć i wdrożyć prosty model uczenia maszynowego przy użyciu Tensorflow.js.

Tensorflow.js

Tensorflow.js to biblioteka JavaScript stworzona przez Google w celu umożliwienia użycia uczenia maszynowego w dowolnej aplikacji internetowej.

W celu dodania wizualizacji w przeglądarce, Tensorflow.js korzysta z małej biblioteki o nazwie tfjs-vis. Można jej następnie użyć do utworzenia bocznego panelu, zwanego Visor, na którym można wyświetlić całą zawartość.

Oprócz tego Google opracował także inną bibliotekę o nazwie ml5.js, która została zaprojektowana tak, aby pozwalała łatwo wdrażać wstępnie wytrenowane modele online. W ten sposób również niespecjaliści mogą rozpocząć wdrażanie modeli uczenia maszynowego w zastosowaniach webowych. Prosty przykład użycia wstępnie wytrenowanego modelu Google PoseNet znajduje się tutaj.

Istnieją 3 główne typy modeli, które można wdrożyć online:

Modele regresyjne

W tym przykładzie postanowiłem wytrenować i zwizualizować prosty model regresji, korzystając ze zbioru danych  “Swedish Committee on Analysis of Risk Premium in Motor Insurance”. Proces trenowania odbywa się w trybie online w czasie rzeczywistym po otwarciu strony internetowej.

Modele klasyfikacyjne

W tym przykładzie zdecydowałem się wytrenować model klasyfikacji przy użyciu “Wine Data Set”. Również w tym przypadku proces trenowania odbywa się w czasie rzeczywistym online. Dodatkowo przedstawiono podsumowanie charakterystyk warstw sieci neuronowej oraz macierzy klasyfikacji.

Wstępnie wytrenowane modele

Na koniec użyłem modelu wstępnie wytrenowanego. W pierwszej kolejności wytrenowałem ten model w Pythonie przy użyciu “Pima Indians Diabetes Database”, zapisałem go jako plik JSON, a na koniec wgrałem go, by był online i mógł zacząć generować przewidywania.

Wszystkie te 3 przykłady są dostępne na mojej stronie internetowej, na wypadek, gdybyś chciał je przetestować.

W tym artykule przeprowadzę Cię przez proces realizacji pierwszego z tych trzech przykładów. Cały kod i zestawy danych użyte do utworzenia tych przykładów są dostępne w moim repozytorium GitHub.

Demonstracja

W tym przykładzie ponownie odniosę się do zbioru danych “Swedish Committee on Analysis of Risk Premium in Motor Insurance”. Składa się on tylko z dwóch kolumn (X = liczba roszczeń i Y = łączna płatność za wszystkie roszczenia w tysiącach koron szwedzkich dla stref geograficznych w Szwecji).

W ramach tej demonstracji spróbujemy przewidzieć całkowitą wielkość  wypłat wszystkich roszczeń, badając dystrybucję całkowitej liczby roszczeń.

Nasz setup

Przede wszystkim musimy utworzyć stronę HTML, na której będą wyświetlane nasze treści. Poniższy fragment kodu stanowi podstawowy szablon. Możesz tam dodać dowolną funkcję.

<!DOCTYPE html>
<html>
<head>
  <title>TensorFlow.js Regression Example</title>

  <!-- Importing TensorFlow.js -->
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tf.min.js"></script>
  <!-- Importing tfjs-vis -->
  <script src="https://cdn.jsdelivr.net/npm/@tensorflow/[email protected]/dist/tfjs-vis.umd.min.js"></script>
  <!-- Importing the main script file -->
  <script src="script.js"></script>

</head>

<body>
</body>
</html>

Przetwarzanie wstępne

Możemy teraz utworzyć inny plik o nazwie script.js (w tym samym katalogu, co nasz plik HTML), w którym możemy rozpocząć ładowanie zestawu danych i wykonać podstawowe zadania związane z przetwarzaniem wstępnym.

Aby załadować zestaw danych z pliku JSON, możemy skorzystać z funkcji getData. Najpierw ładujemy w niej nasz zestaw danych, a następnie wybieramy interesujące nas kolumny i nadajemy im odpowiednią nazwę. Wreszcie za pomocą funkcji filtrowania pozbywamy się wszystkich elementów NaN (nie liczb) obecnych w wybranych kolumnach.

async function getData() {
  const insuranceDataReq = await fetch(
    "https://raw.githubusercontent.com/pierpaolo28/Artificial-Intelligence-Projects/master/Google%20AI%20tools/tensorflow.js/swedish.json"
  );
  const insuranceData = await insuranceDataReq.json();
  const cleaned = insuranceData
    .map(insurance => ({
      claims: insurance.X,
      payment: insurance.Y
    }))
    .filter(insurance => insurance.claims != null && insurance.payment != null);

  return cleaned;
}


Możemy następnie utworzyć funkcję do wstępnego przetwarzania naszego zestawu danych (convertToTensor). W takim przypadku:

  1. Przetasuj dane- losowo uporządkuj próbki, które zostaną później wykorzystane do trenowania naszego modelu regresji. W ten sposób możemy zapewnić naszemu modelowi większą różnorodność danych w porównaniu z pierwotnym rozkładem danych (to dlatego, że nasze dane treningowe będą sukcesywnie dzielone na mniejsze podzbiory zwane partiami, gdy będziemy nimi karmić model ).
  2. Podziel dane na atrybuty i etykiety- atrybuty będą naszymi próbkami wejściowymi, a etykiety naszymi prawdziwymi wartościami wyjściowymi (Ground Truth).
  3. Konwertuj atrybuty i etykiety w postaci tensora- bierzemy nasze oryginalne tablice i przekształcamy je w dwuwymiarowe tensory.
  4. Normalizuj wyjściowe tensory- przeskaluj dane, aby były w zakresie od 0 do 1. Ma to na celu uniknięcie problemów w modelach treningowych, które mają wyjątkowo duże lub małe wartości (przepełnienie / niedopełnienie). Po wytrenowaniu naszego modelu możemy denormalizować to co jest na wyjściu, aby uzyskać ostateczne wyniki.
function convertToTensor(data) {
  // Wrapping these calculations in a tidy will dispose any
  // intermediate tensors.

  return tf.tidy(() => {
    // Step 1. Shuffle the data
    tf.util.shuffle(data);

    // Step 2. Convert data to Tensor
    const inputs = data.map(d => d.claims);
    const labels = data.map(d => d.payment);

    const inputTensor = tf.tensor2d(inputs, [inputs.length, 1]);
    const labelTensor = tf.tensor2d(labels, [labels.length, 1]);

    //Step 3. Normalize the data to the range 0 - 1 using min-max scaling
    const inputMax = inputTensor.max();
    const inputMin = inputTensor.min();
    const labelMax = labelTensor.max();
    const labelMin = labelTensor.min();

    const normalizedInputs = inputTensor
      .sub(inputMin)
      .div(inputMax.sub(inputMin));
    const normalizedLabels = labelTensor
      .sub(labelMin)
      .div(labelMax.sub(labelMin));

    return {
      inputs: normalizedInputs,
      labels: normalizedLabels,
      // Return the min/max bounds so we can use them later.
      inputMax,
      inputMin,
      labelMax,
      labelMin
    };
  });
}

Definiowanie i trenowanie naszego modelu

Możemy teraz zaprojektować prosty model uczenia maszynowego (createModel), a następnie stworzyć funkcję trenującą go (trianModel).

W tym przypadku skorzystałem z funkcji callback, aby wywołać bibliotekę obsługi wykresów Tensorflow.js o nazwie tfjs-vis, by zobaczyć na wykresie jak zmieniała się strata podczas treningu modelu w czasie rzeczywistym. Jeśli chcesz dowiedzieć się więcej na temat działania callbacka, przedstawiłem bardziej szczegółowe wyjaśnienie na ich temat w innym artykule.

function createModel() {
  // Create a sequential model
  const model = tf.sequential();

  // Add two hidden layers
  model.add(tf.layers.dense({ inputShape: [1], units: 5, useBias: true }));
  model.add(tf.layers.dense({ units: 10, useBias: true }));
  // Add an output layer
  model.add(tf.layers.dense({ units: 1, useBias: true }));

  return model;
}

async function trainModel(model, inputs, labels) {
  // Prepare the model for training.
  model.compile({
    optimizer: tf.train.adam(),
    loss: tf.losses.meanSquaredError,
    metrics: ["mse"]
  });

  const batchSize = 32;
  const epochs = 50;

  return await model.fit(inputs, labels, {
    batchSize,
    epochs,
    shuffle: true,
    callbacks: tfvis.show.fitCallbacks(
      { name: "Training Progresses" },
      ["loss", "mse"],
      { height: 200, callbacks: ["onEpochEnd"] }
    )
  });
}

Testowanie

Teraz, gdy nasz model jest wytrenowany, możemy zacząć prognozowanie. Aby to zrobić, zaprojektowałem funkcję testModel. W tym prostym przykładzie postanowiłem wykorzystać dane treningowe do przetestowania wydajności naszego modelu.

Dodatkowo zdenormalizowałem nasze prognozy wygenerowane przez model, a następnie wykreśliłem je na podstawie rzeczywistych wyników, aby sprawdzić wydajność modelu.

function testModel(model, input, inputData, normalizationData) {
  const { inputMax, inputMin, labelMin, labelMax } = normalizationData;

  // Generate predictions for a uniform range of numbers between 0 and 1;
  // We un-normalize the data by doing the inverse of the min-max scaling
  // that we did earlier.
  const [xs, preds] = tf.tidy(() => {
    //const xs = tf.linspace(0, 1, 63); //Using same fake data to test the model
    const xs = input; // Using the input data to test the model
    const preds = model.predict(xs.reshape([63, 1]));

    const unNormXs = xs.mul(inputMax.sub(inputMin)).add(inputMin);

    const unNormPreds = preds.mul(labelMax.sub(labelMin)).add(labelMin);

    // Un-normalize the data
    return [unNormXs.dataSync(), unNormPreds.dataSync()];
  });

  const predictedPoints = Array.from(xs).map((val, i) => {
    return { x: val, y: preds[i] };
  });

  const originalPoints = inputData.map(d => ({
    x: d.claims,
    y: d.payment
  }));

  tfvis.render.scatterplot(
    { name: "Model Predictions vs Original Data" },
    {
      values: [originalPoints, predictedPoints],
      series: ["original", "predicted"]
    },
    {
      xLabel: "Number of claims",
      yLabel: "Total Payment",
      height: 300
    }
  );
}

Uruchamianie modelu

Musimy teraz utworzyć funkcję run, aby cały nasz kod działał. Najpierw wykonujemy w niej wizualizację danych, a następnie wywołujemy wszystkie funkcje, które zdefiniowaliśmy wcześniej.

async function run() {
  // Load and plot the original input data that we are going to train on.
  const data = await getData();
  const values = data.map(d => ({
    x: d.claims,
    y: d.payment
  }));

  tfvis.render.scatterplot(
    { name: "Number of claims vs Total Payment" },
    { values },
    {
      xLabel: "Number of claims",
      yLabel: "Total Payment",
      height: 300
    }
  );

  // Create the model
  const model = createModel();
  tfvis.show.modelSummary({ name: "Model Summary" }, model);

  // Convert the data to a form we can use for training.
  const tensorData = convertToTensor(data);
  const { inputs, labels } = tensorData;

  // Train the model
  await trainModel(model, inputs, labels);
  console.log("Done Training");

  // Make some predictions using the model and compare them to the
  // original data
  testModel(model, inputs, data, tensorData);
}

document.addEventListener("DOMContentLoaded", function() {
  run();
});
async function run() {
  // Load and plot the original input data that we are going to train on.
  const data = await getData();
  const values = data.map(d => ({
    x: d.claims,
    y: d.payment
  }));

  tfvis.render.scatterplot(
    { name: "Number of claims vs Total Payment" },
    { values },
    {
      xLabel: "Number of claims",
      yLabel: "Total Payment",
      height: 300
    }
  );

  // Create the model
  const model = createModel();
  tfvis.show.modelSummary({ name: "Model Summary" }, model);

  // Convert the data to a form we can use for training.
  const tensorData = convertToTensor(data);
  const { inputs, labels } = tensorData;

  // Train the model
  await trainModel(model, inputs, labels);
  console.log("Done Training");

  // Make some predictions using the model and compare them to the
  // original data
  testModel(model, inputs, data, tensorData);
}

document.addEventListener("DOMContentLoaded", function() {
  run();
});


Możesz teraz przetestować swój model, uruchamiając go na własnej stronie internetowej lub za pomocą lokalnego serwera. W przypadku, gdy nigdy nie korzystałeś z lokalnego serwera, jednym prostym rozwiązaniem może być użycie Visual Studio Code i zainstalowanie Live Server extension.

<p>Loading...</p>