본문으로 바로가기

TensorFlow.js 찍먹하기

category 🤖 ML in browser/🧱 TensorFlow.js 2021. 1. 8. 10:14

공부 자료들

공식 홈페이지 : www.tensorflow.org/js?hl=ko

example : github.com/tensorflow/tfjs-examples

 

tensorflow 관련 문서, 튜토리얼 등을 모아 놓은 README.md : github.com/tensorflow/tfjs/blob/master/GALLERY.md

 

 

사실 javascript 기반 머신러닝 라이브러리 자체는 brain.jsconvnetjs 등 존재한다.

Tensorflow를 기반으로 만들어진 간략한 머신러닝 라이브러리인 ml5js도 좋은 옵션이다.

 

ML의 절차를 거칠게 요약하자면 아래와 같습니다.

1. 데이터 준비 
2. 모델 생성 
3. 모델을 데이터에 맞게 fitting
4. 사용, 재학습

 

그러나 어떤 목적을 달성하기 위해 처음부터 모델을 만들 필요는 없습니다. tensorflow에서는 이미 학습된 모델을 제공합니다.

자신의 프로젝트에 적합하지 않다면 기존 모델을 재학습시킬 수도 있습니다.

 

 

설치

www.tensorflow.org/js/tutorials/setup

// 순수 js 버전. 가장 느림
yarn add @tensorflow/tfjs

// 네이티브 C ++ 바인딩으로 TensorFlow.js를 설치
yarn add @tensorflow/tfjs-node

// (Linux 전용) CUDA를 지원 하는 NVIDIA® GPU가있는 경우 더 높은 성능을 위해 GPU 패키지를 사용.
yarn add @tensorflow/tfjs-node-gpu

 

CDN

<head>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@2.0.0/dist/tf.min.js"></script>
</head>
<body>
    <script>
        console.log(tf) //tf 전역 변수
    </script>
</body>

 

* Typescript를 사용하고 있는 경우

TypeScript를 사용할 때 프로젝트에서 엄격한 null 검사를 사용하거나 컴파일 중에 오류가 발생하는 경우 tsconfig.json 파일에서 skipLibCheck: true 를 설정해야 할 수 있습니다

 

+ 최근에 확인해보니 tensorflow.js도 typescript로 다시 만들어진듯? 위의 절차는 필요 없다.

 

 

기존 모델을 이용한 Hello World

단순히, model 패키지를 다운로드 받고, 특정 img DOM을 전달하기만 하면, classify를 해주는 코드를 짰습니다.

tensorflow.js에서 제공하는 다른 모델도 이와 같은 방식으로 사용할 수 있습니다.

 

 

github.com/tensorflow/tfjs-models/tree/master/mobilenet

<img id="img" src="director.jpeg"></img>
import * as tf from '@tensorflow/tfjs';
const mobilenet = require('@tensorflow-models/mobilenet');

const img = document.getElementById('img');

// Load the model.
const model = await mobilenet.load();

// Classify the image.
const predictions = await model.classify(img);

console.log('Predictions: ');
console.log(predictions);

 

 

자체 Model 생성하기

www.tensorflow.org/js/guide/train_models#modelfit

 

훈련 모델  |  TensorFlow.js

이 가이드는 이미 모델 및 레이어 가이드를 읽었다 고 가정합니다. TensorFlow.js에는 기계 학습 모델을 학습시키는 두 가지 방법이 있습니다. LayersModel.fit() 또는 LayersModel.fitDataset() 와 함께 Layers API

www.tensorflow.org

 

데이터를 준비하여, tensor로 만들고

해당 tensor를 이용해 모델을 만든 후에

학습시키면 끝납니다.

 

// const tf = require("@tensorflow/tfjs");
const tf = require("@tensorflow/tfjs-node");

console.log(tf ? "tf loaded" : "error");

// 1. 데이터를 준비
const temperature = [20, 21, 22, 23]; // data
const selling = [40, 42, 44, 46]; // label

// tensor(t)를 만듭니다.
const data = tf.tensor(temperature);
const label = tf.tensor(selling);
console.log(Object.keys(data)); // tensor 객체의 key :  isDisposedInternal, shape, dtype, size, strides, dataId, id, rankType

// 2. 모델의 모양을 만듭니다.
const X = tf.input({ shape: [1] }); // lf 객체. 1차원 배열이므로 1
const Y = tf.layers.dense({ units: 1 }).apply(X); // lf 객체
const model = tf.model({ inputs: X, outputs: Y }); // e 객체
// ModelCompileArgs 에는 optimizer,loss,metric 존재하며 정의 부분 참고할 것
model.compile({ optimizer: tf.train.adam(), loss: tf.losses.meanSquaredError });

// 3. 학습 시작
// epochs를 100, batch를 2로 잡고, 각 batch가 끝날 때마다 log를 찍음
// ModelFitArgs는 내부 정의를 살펴보도록
const onBatchEnd = function (epoch, logs) {
  console.log("epoch", epoch, logs);
};

// fit에 Tensor를 넣어야한다
model
  .fit(data, label, {
    epochs: 100,
    batchSize: 2,
    callbacks: { onBatchEnd },
  })
  .then((info) => {
    console.log(info);

    // 기존의 데이터를 학습된 모델에 적용해보자
    const predictedOutput = model.predict(data);
    predictedOutput.print();
  });
// 학습된 모델을 이용하여 선형 회귀 예측
var NextWeekTemp = [15, 16, 17, 18, 19];
var NextWeekInput = tf.tensor(NextWeekTemp);
var NextWeekOutput = model.predict(NextWeekInput);
NextWeekOutput.print();

 

+

fitParams 에 줄 수 있는 콜백을 찍어보니 다음과 같았다.

export declare abstract class BaseCallback {
    validationData: Tensor | Tensor[];
    /**
     * Training parameters (eg. verbosity, batch size, number of epochs...).
     */
    params: Params;
    setParams(params: Params): void;
    onEpochBegin(epoch: number, logs?: UnresolvedLogs): Promise<void>;
    onEpochEnd(epoch: number, logs?: UnresolvedLogs): Promise<void>;
    onBatchBegin(batch: number, logs?: UnresolvedLogs): Promise<void>;
    onBatchEnd(batch: number, logs?: UnresolvedLogs): Promise<void>;
    onTrainBegin(logs?: UnresolvedLogs): Promise<void>;
    onTrainEnd(logs?: UnresolvedLogs): Promise<void>;
    setModel(model: Container): void;
}

 

최적화 도구(optimizer), 손실(loss) 및 측정 항목(metrics)

 

www.tensorflow.org/js/guide/train_models

 

훈련 모델  |  TensorFlow.js

이 가이드는 이미 모델 및 레이어 가이드를 읽었다 고 가정합니다. TensorFlow.js에는 기계 학습 모델을 학습시키는 두 가지 방법이 있습니다. LayersModel.fit() 또는 LayersModel.fitDataset() 와 함께 Layers API

www.tensorflow.org

최적화 도구, 손실 및 측정 항목을 설정하여 모델을 컴파일합니다.

각 특성이나, 사용한 알고리즘은 공식문서를 참고하도록합시다.

 

 

- Optimizer

 

js.tensorflow.org/api/latest/#Training-Optimizers

 

TensorFlow.js | 자바스크립트 개발자를 위한 머신러닝

브라우저, Node.js 또는 Google Cloud Platform에서 모델을 학습시키고 배포합니다. TensorFlow.js는 자바스크립트 및 웹 개발을 위한 오픈소스 ML 플랫폼입니다.

www.tensorflow.org

tf.train.sgd
tf.train.momentum
tf.train.adagrad
tf.train.adadelta
tf.train.adam
tf.train.adamax
tf.train.rmsprop

 

- loss

 

js.tensorflow.org/api/latest/#Training-Losses

 

TensorFlow.js | 자바스크립트 개발자를 위한 머신러닝

브라우저, Node.js 또는 Google Cloud Platform에서 모델을 학습시키고 배포합니다. TensorFlow.js는 자바스크립트 및 웹 개발을 위한 오픈소스 ML 플랫폼입니다.

www.tensorflow.org

tf.losses.absoluteDifference
tf.losses.computeWeightedLoss
tf.losses.cosineDistance
tf.losses.hingeLoss
tf.losses.huberLoss
tf.losses.logLoss
tf.losses.meanSquaredError
tf.losses.sigmoidCrossEntropy
tf.losses.softmaxCrossEntropy

 

fitting param에 넘겨주는 콜백의 인자 중 logs는 loss 값을 가지고 있다.

이 loss값이 0에 가까울수록 정확한 모델이라는 의미이다.

평균 제곱 오차(Mean Squared Error, MSE) = sum(loss^2) / num 
평균 제곱근 오차(Root Mean Squared Error, RMSE) = MSE^1/2

아래 예시에는 MSE를 loss로 사용합니다.

const X = tf.input({ shape: [1] }); // lf 객체. 1차원 배열이므로 1
const Y = tf.layers.dense({ units: 1 }).apply(X); // lf 객체
const SellingPredictModel = tf.model({ inputs: X, outputs: Y }); // e 객체

// ModelCompileArgs 에는 optimizer,loss,metric 존재하며 정의 부분 참고할 것
// loss 측정은 MSE로 하기로 하자.

SellingPredictModel.compile({
  optimizer: tf.train.adam(),
  loss: tf.losses.meanSquaredError,
});

 

RMSE를 측정하기 위해 각 epoch가 끝날 때마다 실행되는 콜백에서 loss 값을 가져와 가공하였습니다.

SellingPredictModel.fit(data, label, {
  epochs: 100,
  callbacks: {
    onEpochEnd: (epoch, logs) => {
      console.log(epoch, `RMSE : ${Math.sqrt(logs.loss)}`);
    },
  },
}

 

 

weight와 bias 확인하기

선형 회귀이므로 y = wx + b 꼴로 나타낼 수 있을텐데, weight와 bias 값을 추출해보도록하자.

 

* arraySync는 '동기'이고, node는 싱글 스레드이므로 blocking을 자제해야 한다. async하게 작동하는 array를 사용해야 맞겠으나, 간단한 확인이므로 arraySync를 사용했다. 실제 프로젝트에서는 사용하지 말자.

SellingPredictModel.fit(data, label, {
  epochs: 100,
  callbacks: {
    onEpochEnd: (epoch, logs) => {
      console.log(epoch, `RMSE : ${Math.sqrt(logs.loss)}`);
    },
  },
}).then((info) => {
  console.log(`${info.params.epochs} epochs end`);

  // 기존의 데이터를 학습된 모델에 적용해보자
  const predictedOutput = SellingPredictModel.predict(data);
  predictedOutput.print();

  // 그래서, weight와 bias가 얼마일까
  // weight. [[]] 꼴이라서 flatten도 해줬습니다.
  console.log(SellingPredictModel.getWeights()); // [tensor, tensor] 반환. 첫번째가 weight, 두번재가 bias
  console.log(SellingPredictModel.getWeights()[0].arraySync().flat(1)); // Returns the flattened data that backs the tensor.

  // bias
  console.log(SellingPredictModel.getWeights()[1].arraySync().flat(1));
});

 

 

학습된 모델의 저장과 로드

www.tensorflow.org/js/guide/save_load

 

모델 저장 및로드  |  TensorFlow.js

TensorFlow.js는 Layers API로 생성되었거나 기존 TensorFlow 모델에서 변환 된 모델을 저장하고로드하는 기능을 제공합니다. 이것은 여러분이 직접 훈련 한 모델이거나 다른 사람들이 훈련 한 모델 일 수

www.tensorflow.org

결국에 학습한 y = wx + b 꼴의 모델은, w와 b만 저장해두고, 간단한 연산을 통해 ML을 적용한 결과값을 사용할 수 있다. 다른 모델도 마찬가지로, 피팅을 통해 도출된 값만 따로 빼서 간단한 연산을 통과시킴으로써 모델을 사용하는 방식으로 프로젝트에 적용합니다.

 

  • localStorage (Browser) 
  • IndexedDB (Browser)
  • file로 download하기 (Browser)
  • http(s) 요청으로 보내기 (원격 서버에 저장하는 방법. ML용 서버를 따로 돌리고 있을 경우, 다른 서버로 전송)
  • 네이티브 파일 시스템 (Node)

각 방법들은 대동소이합니다.

 

localstorage를 사용하면 다음과 같이 저장됩니다.

 

저는 Node를 사용했으므로 네이티브 파일 시스템을 사용할 수밖에 없습니다.

SellingPredictModel.save("file://SellingPredictModel");

저장된 결과 model.json과 weights.bin이 나왔습니다.

  1. 아래 설명 된 가중치 파일에 대한 토폴로지 및 참조를 전달하는 [model].json 이라는 텍스트 JSON 파일입니다.
  2. [model].weights.bin 이라는 가중치 값을 포함하는 이진 파일입니다.

두 파일의 이름은 항상 위에 지정된 것과 정확히 일치합니다 (이름은 함수에 내장 됨).

 

저장한 파일을 불러오기 위해서는 아래처럼 해주면 됩니다. 간단하죠

const model = await tf.loadLayersModel('file://path/to/my-model/model.json');

 

이를 활용하여, node에서 model.json을 통해 학습된 모델을 사용하여, 선형 회귀 분석을 진행해보았습니다.

const tf = require("@tensorflow/tfjs-node");

async function loadModel(NextWeekTemp) {
  const model = await tf.loadLayersModel(
    "file://SellingPredictModel/model.json"
  );

  const NextWeekInput = tf.tensor(NextWeekTemp);
  const NextWeekOutput = model.predict(NextWeekInput);
  NextWeekOutput.print();
}

loadModel([15, 16, 17, 18, 19]);

 

 

CPU instruction 관련 경구 문구

텐서플로우를 쓰다보면 아래와 같은 문구를 마주칠 때가 있습니다.

tensorflow/core/platform/cpu_feature_guard.cc:142]
Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2

 

의역하자면, 현재 환경에서 사용 중인 CPUtensorflow 바이너리가 사용할 수도 있는 명령어(AVX2)를 지원한다따라서, 더 빨라질 수도 있으니까 알아두라는 겁니다.

 

최근 CPUAVX, AVX2, FMA, SSE4와 같은 명령어(instruction)을 제공합니다. CPU가 선형 대수 관련 연산을 더 빠르게 수행할 수 있도록 설계한 것인데, 머신러닝 알고리즘에서 션형대수 연산을 자주 사용하기 때문에 모델 학습 시간이 줄어들게 되는 장점이 있습니다.

 

* CPU의 명령어에 관한 간략한 이해는 제가 작성한 포스트 https://darrengwon.tistory.com/1089?category=911757 여기를 참고하면 이해할 수 있습니다.

 

그러나 배포되는 tensorflow에서 특정 CPU에서만 제공하는 명령어를 사용하게 될 경우, 환경에 따라 작동하지 않을 수 있으므로 사용하지 않고 가급적 많은 환경에서 구동할 수 있도록 만들어 졌습니다.

 

그래서 결론은, (1)단순히 무시하거나, (2)실행 환경에 맞게 tensorflow binary를 컴파일하여 cpu 연산 속도에서 이득을 보는 방법이 있겠습니다. tensorflow를 다시 컴파일하는 방법에 대해서는 https://github.com/tensorflow/tensorflow/issues/8037 여기를 참고해봅시다.

'🤖 ML in browser > 🧱 TensorFlow.js' 카테고리의 다른 글

deep learning POC  (0) 2021.01.08
보스턴 집값 예측을 tensorflow.js로 해보자  (0) 2021.01.08

darren, dev blog
블로그 이미지 DarrenKwonDev 님의 블로그
VISITOR 오늘 / 전체