곡선을 2차원 데이터에 맞추기 (make predictions from 2d data)

data load and visualize

실행환경

web browser

import

<!DOCTYPE html> <html> <head> <title>TensorFlow.js Tutorial</title> <!-- Import TensorFlow.js --> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"></script> <!-- Import tfjs-vis 시각화를 위한 라이브러리--> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-vis@1.0.2/dist/tfjs-vis.umd.min.js"></script> <!-- Import the main script file --> <script src="script.js"></script> </head> <body> </body> </html>
HTML
복사

visualize

//script.js async function run() { // Load and plot the original input data that we are going to train on. const data = await getData(); // 데이터 형태는 { horsepower:number; mpg: number }[] 이다 console.log(data); const values = data.map((d) => ({ x: d.horsepower, y: d.mpg, })); tfvis.render.scatterplot( { name: "Horsepower v MPG" }, { values }, { xLabel: "Horsepower", yLabel: "MPG", height: 300, } ); // More code will be added below } async function getData() { const carsDataReq = await fetch( "https://storage.googleapis.com/tfjs-tutorials/carsData.json" ); const carsData = await carsDataReq.json(); const cleaned = carsData .map((car) => ({ mpg: car.Miles_per_Gallon, horsepower: car.Horsepower, })) .filter((car) => car.mpg != null && car.horsepower != null); return cleaned; } run();
JavaScript
복사

Define the model architecture

function createModel() { const model = tf.sequential(); // 모델을 인스턴스화 (tf.Model 객체 생성) // Add a single input layer model.add(tf.layers.dense({inputShape: [1], units: 1, useBias: true})); // Add an output layer model.add(tf.layers.dense({units: 1, useBias: true})); return model; }
JavaScript
복사
sequential 은 model 을 인스턴스화 한다.
첫번째 model.add 에서는 인풋 레이어를 추가하는 것이다. 이 레이어는 자동으로 dense 레이어와 연결되는데, 이 dense 레이어는 Wx + b 의 형태로 나타난다.
sequential 모델은 inputShape를 정의 해야 한다. 현재 데이터에서는 [1]로 정의했는데 x값이 한개 밖에 없기 때문이다.(horsepower)
unints 는 해당 레이어에 weight 의 크기를 설정한다.
useBias 는 bias(Wx + b 에서 b)를 사용할 것인지 사용하는 옵션이다. 기본값으로 true 인데 굳이 true로 했다.
마지막 model.add 에서는 모델에 output 레이어를 추가하는 것이다. units 를 1로 설정한 것은 output 이 1개의 number 를 반환할 것이기 때문이다. (목표는 input: horsepower, output: mps !)

Visualize Model!

const model = createModel(); tfvis.show.modelSummary({name: 'Model Summary'}, model);
JavaScript
복사

Training 을 위한 데이터 preset!

성능적인 이점을 얻기 위해서는 데이터를 tensor 로 변환할 필요가 있다. 그리고 shuffling 과 nomalization 을 수행할 것이다.(best practice)
function convertToTensor(data) { return tf.tidy(() => { // Step 1. Shuffle the data tf.util.shuffle(data); // Step 2. Convert data to Tensor const inputs = data.map(d => d.horsepower) const labels = data.map(d => d.mpg); 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, } }); }
JavaScript
복사

shuffling!

shuffle 은 모델에 학습시킬 데이터의 순서를 randomize 한다.
데이터 셋의 하위 집합인 배치에 고른 분포를 줄 수 있기 때문에 이 순서를 shuffle 하는 것이 중요하다. (데이터의 순서에 따라 학습의 결과가 달라지지 않도록!)
항상 모델을 학습시키기 전에 shuffling 하는 것이 Best Practice 다!

convert to tensors

tensor2d 를 통해서 2d 형태의 텐서를 생성한다.
inputs 과 labels 를 생성해준다.
각 tensor 는 [예시의 갯수, 하나의 예시당 특성의 갯수]의 형태로 나타난다.

normalize the data

위의 코드에서는 데이터들을 min-max scailing 을 사용해서 0 ~ 1의 범위의 데이터들로 변환한다.
tensorflow.js 로 빌드할 머신러닝 모델의 내부는 너무 크지 않은 숫자로 작동하도록 설계되었으므로 normalization 이 중요하다!
이 또한 Best Practice
데이터를 텐서로 만들기 전에도 정규화 할 수 있지만, 백터화(tensor)를 활용하면 for 루프를 사용하지 않고도 스케일링 작업을 수행할 수 있으므로 나중에 수행하는 것이 편할 것이다!
마지막에 정규화된 값들과 함께 정규화 범위도 반환한다. 이는 훈련중에 정규화에 사용한 값을 유지해서 출력을 정규화하지 않고 원래 스케일로 출력할 수 있도록 하기 위해서, 그리고 추가로 입력되는 데이터들을 동일한 방식으로 정규화 할 수 있도록 한다!

Training the 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 Performance' }, ['loss', 'mse'], { height: 200, callbacks: ['onEpochEnd'] } ) }); }
JavaScript
복사

prepare training

optimizer: example 을 통해서 model 을 update 를 제어하는 알고리즘. 해당 코드에서는 adam 알고리즘을 사용했다
loss: 모델에게 제공된 데이터 batch 들을 잘 학습하고 있는지 알려주는 함수. 위에서는 평균 제곱 오차를 사용하고있다.
batchSize 는 32로 설정. 보통 32 ~512 범위에 있다. 모든 문제에 이상적인 배치 크기는 없다! 데이터 셋의 크기와 목적에 맞게 설정하는 것이 좋다!
epochs 는 모델이 데이터 세트를 보려는 횟수를 나타낸다. 위 코드에서는 데이터 세트를 50번 반복해서 볼 것.
fix 함수를 통해서 학습 시작
학습 진행 상황을 모니터링 하기 위해 callback 으로 visualize 함수를 내보낸다
// run 함수에 추가 // 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');
JavaScript
복사

Prediction

이제 학습된 모델을 통해서 예측을 해보자!!!
function testModel(model, 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, 100); const preds = model.predict(xs.reshape([100, 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.horsepower, y: d.mpg, })); tfvis.render.scatterplot( {name: 'Model Predictions vs Original Data'}, {values: [originalPoints, predictedPoints], series: ['original', 'predicted']}, { xLabel: 'Horsepower', yLabel: 'MPG', height: 300 } ); }
JavaScript
복사
lintspace 를 통해서 모델에 공급할 100개의 새로운 예(example)을 생성한다
predict 를 통해서 model 에 예를 전달한다
테이터를 normaiize 된 값이 아닌 원본 값을 가져오도록 역 정규화를 한다.
tensor.dattaSync 를 통해서
// run 코드에 추가한다 testModel(model, data, tensorData);
JavaScript
복사

용어

텐서(tensor)

텐서플로우 프로그램의 기본 데이터 구조입니다. 텐서는 대부분 스칼라, 벡터 또는 행렬로 이루어진 N차원 데이터 구조이며, N은 매우 큰 수일 수 있습니다. 텐서의 요소는 정수, 부동 소수점 또는 문자열 값을 포함할 수 있습니다.

정규화(normalization)

실제 값 범위를 표준 값 범위(일반적으로 -1~+1 또는 0~1)로 변환하는 과정입니다. 예를 들어 어떤 특성의 원래 범위가 800~6,000인 경우, 뺄셈과 나눗셈을 거쳐 값 범위를 -1~+1로 정규화할 수 있습니다.

옵티마이저(optimizer)

경사하강법 알고리즘의 구체적인 구현입니다. 옵티마이저에 대한 텐서플로우의 기본 클래스는 tf.train.Optimizer입니다. 다른 옵티마이저는 다음 중 하나 이상의 개념을 활용하여 주어진 학습 세트에서 경사하강법의 효과를 강화할 수 있습니다.

평균 제곱 오차(MSE, Mean Squared Error)

예시당 평균 제곱 손실입니다. MSE는 제곱 손실을 예시의 개수로 나누어 계산합니다. 텐서플로우 플레이그라운드에서 '학습 손실' 및 '테스트 손실'로 표시하는 값이 MSE입니다.

세대(epoch)

전체 데이터 세트의 각 예를 한 번씩 확인한 전체 학습 단계입니다. 따라서 한 세대(이폭)는 N/배치 크기 학습 반복을 나타내며, 여기에서 N은 총 예시 수입니다.