TensorFlow.js 으로 ML 모델 만들기

Jieun Lee·2021년 5월 13일
0

TensorFlow.js

목록 보기
1/2
post-thumbnail

TensorFlow.js

이 글은 유투브 채널 "생활코딩" 의 TensorFlow.js 강의를 기반으로 작성하였습니다.

machine-learning

우리가 흔히 얘기하는 Machine Learning 이라고 하는 분야는 비 전공자들 에게는 조금 막연하고 어렵게 느껴질 수 있다. 하지만 이론을 자세히 알지 못해도 데이터와 간단한 도구를 가지고 우리는 Learning Model을 만들고 이를 활용할 수 있다.

이러한 모델을 만들어주는 도구 중에는 TensorFlow가 있는데, TensorFlow는 일반적으로 python 으로 사용하지만 javascript 를 활용해서도 데이터를 학습시켜 간단한 모델을 만들어 볼 수 있다.

Deep Learning 이란?

deep-learning

인공 신경망을 깊게 쌓아서 만들었다는 표현으로 Deep Learning 이라는 용어를 사용하며 같은 의미로 인간의 뇌를 이루고 있는 신경 세포인 neuron에서 따온 Neural Network(인공 신경망, Deep Learning)는 인간의 신경을 모방한 알고리즘을 가리키는 말이다.

TensorFlow 란?

Deep Learning의 원리를 몰라도 코드를 작성하여 문제를 해결할 수 있도록 알고리즘을 제공하는 라이브러리들이 있다.

  • TensorFlow

  • ConvNetJS

  • Brain.js

  • Caffe2

  • PyTorch

  • theano

    등등..

TensorFlow.js 는 javascript 실행 환경인 브라우저나 Node.js에서 학습시켜 배포할 수 있다. 특별한 SW 를 설치하지 않고도 동작 가능하며 사용자의 컴퓨팅 파워를 이용할 수 있어 비용을 절약할 수 있다.

지도 학습

Machine Learning 에는 지도 학습, 비지도 학습, 강화 학습과 같은 기술들이 있다. 그 중에서도 지도 학습은 데이터가 값의 형태일 때 사용하는데 회귀라는 방법으로 학습하게 된다. 회귀 과정은 아래와 같다.

  1. 과거의 데이터를 준비한다.
  2. 모델의 모양을 만든다.
  3. 데이터로 모델을 학습(FIT)한다.
  4. 모델을 이용한다.

<TensorFlow에서 모델을 이용하는 3가지 방법>

  • 기존 모델 실행
  • 기존 모델 다시 학습시키기
  • 자바스크립트로 ML 개발

기존 모델 실행

TensorFlow 공식 사이트에는 다양한 javascript 라이브러리와 모델들을 제공하고 있다. 이미 학습된 모델들도 제공하는데 이미지 분류, 객체 감지, 신체 분절화, 자세 추정, 악성 텍스트 감지 등 주로 이미지 처리 관련 모델들이 제공되고 있다.

<이미지 분류 예제>

<!--mobilenet.html-->

<!DOCTYPE html>
<html>
<body>
    <!-- Load TensorFlow.js. This is required to use MobileNet. -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.1"> </script>
    <!-- Load the MobileNet model. -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow-models/mobilenet@1.0.0"> </script>
    <!-- Replace this with your image. Make sure CORS settings allow reading the image! -->
    <img id="img" src="penguin.jpg"></img>
    <!-- Place your code in the script tag below. You can also use an external .js file -->
    <script>
        ...
    </script>
</body>
</html>
...
        // Notice there is no 'import' statement. 'mobilenet' and 'tf' is
        // available on the index-page because of the script tag above.
        const img = document.getElementById('img');
        // Load the model.
        mobilenet.load().then(model => {
            // Classify the image.
            model.classify(img).then(predictions => {
                console.log('Predictions: ');
                console.log(predictions);
            });
        });
...

나의 모델 만들기

레모네이드를 판매하는 장사를 한다고 가정해보자. 레모네이드 판매량이 그 날 온도에 영향을 미친다면 내일 몇 개의 레모네이드가 판매될 지 예측하는 모델을 어떻게 만들 수 있을까?

기본적인 모델을 만드는 과정은 아래 그림과 같다.

how-to-learn-model

직접 모델을 만들어 보기 위해 일단 환경 설정이 필요하다.

  1. Node.js 로 실행
//pure javascript 
npm install @tensorflow/tfjs

//c++ binding
npm install @tensorflow/tfjs-node

//using gpu
npm install @tensorflow/tfjs-node-gpu
  1. 웹브라우저에서 실행
<!DOCTYPE html>
<html>
<head>
    <title>TensorFlow.js Tutorial - lemon</title>

    <!-- Import TensorFlow.js -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"></script>

</head>

<body>
    <script>
        ...
    </script>
</body>

</html>

이제 레모네이드 판매량 데이터를 학습하여 모델을 만드는 코드를 작성해보자.

...
        // 1. 과거의 데이터를 준비합니다.
        var 온도 = [20, 21, 22, 23];
        var 판매량 = [40, 42, 44, 46];
        var 원인 = tf.tensor(온도);
        var 결과 = tf.tensor(판매량);

        // 2. 모델의 모양을 만듭니다.
        var X = tf.input({ shape: [1] });
        var Y = tf.layers.dense({ units: 1 }).apply(X);
        var model = tf.model({ inputs: X, outputs: Y });
        var compileParam = { optimizer: tf.train.adam(), loss: tf.losses.meanSquaredError }
        model.compile(compileParam);

        // 3. 데이터로 모델을 학습시킵니다.
        var fitParam = { epochs: 100 };
        model.fit(원인, 결과, fitParam).then(function (result) {
            // 4. 모델을 이용합니다.
            // 4.1 기존의 데이터를 이용
            var 예측한결과 = model.predict(원인);
            예측한결과.print();
        });

        // 4.2 새로운 데이터를 이용
        var 다음주온도 = [15, 16, 17, 18, 19]
        var 다음주원인 = tf.tensor(다음주온도);
        var 다음주결과 = model.predict(다음주원인);
        다음주결과.print();
...

과거의 데이터를 준비한다.

  • 데이터를 학습시키기 위해 tensor() 함수를 이용해 array 형태의 input 데이터를 변환해주어야 한다.

모델의 모양을 만든다.

  • input

    tf.input({ shape: [input 데이터 컬럼 수] })
  • output

     tf.layers.dense({ units: (output 데이터 컬럼 수)).apply(input)
  • compile

    • optimizer: 모델을 학습시키는 방법으로 손실 함수를 통해 모델을 업데이트 하는 방식 (e.g. adam)
    • loss: 모델이 얼마나 정확한지 측정하는 방법 (e.g. mean squared error: 평균 제곱 오차)

데이터로 모델을 학습(FIT)한다.

  • epochs: 데이터를 학습하고자 하는 횟수
  • 얼마나 학습 됐는지 확인하는 방법
...
var fitParam = {
    epochs: 100,
    callbacks: {
        onEpochEnd: function (epoch, logs) {
            console.log('epoch', epoch, logs, "RMSE --> ", Math.sqrt(logs.loss));
        }
    }
}
...

모델을 이용한다.

  • predict: 만들어진 모델을 이용하여 원인 데이터에 따른 결과를 예측하는 함수

<tensor에서 원시값 가져오기>

  • 학습 시 사용하는 원인 값과 예측으로 나온 결과 값은 데이터 타입이 tensor 형상인데 아래 메서드를 사용하면 원시값을 가져올 수 있다.
  • array() / data()
  • arraySync() / dataSync(): 성능 문제를 일으킬 수 있어 운영 어플리케이션에는 적합하지 않음
//asynchronous
model.predict(tf.tensor([20])).array().then(array => console.log(array[0][0]));
//synchronous
model.predict(tf.tensor([20]).arraySync()[0][0]

<weight/bias 확인하기>

weight-bias

var weights = model.getWeights()
var weight = weights[0].arraySync()[0][0]
var bias = weights[1].arraySync()[0]

모델의 저장과 불러오기

학습된 모델은 브라우저의 로컬 저장소나 IndexedDB에 저장하거나 파일로 다운로드 할 수도 있다. 또한 원격 서버에 업로드 하거나 Node.js로 실행하면 파일 시스템에 직접 액세스 하여 모델을 저장할 수 있다.

모델 저장하기 및 불러오기

보스톤 집값 구하기

아래는 1978년 미국 보스톤 주의 506개 타운들의 집 값을 정리한 표와 집 값을 구하는 식을 나타낸다. 표에 각각의 행은 타운을 나타하고 열은 각 타운의 특성을 나타낸다.

get-price-of-houses-in-boston

MEDV는 타운에 있는 주택들의 중앙 값을 나타낸다. 14번 열은 종속변수이고 1~13번 열은 14번 열인 집 값에 영향을 미치는 독립 변수들이라고 한다면 아래는 보스톤 집 값을 구하는 모델을 학습하고 예측하는 코드이다.

10.3.html

var 보스톤_원인 = [
    [0.00632,18,2.31,0,0.538,6.575,65.2,4.09,1,296,15.3,396.9,4.98],
    [0.02731,0,7.07,0,0.469,6.421,78.9,4.9671,2,242,17.8,396.9,9.14]
    ...
]
var 보스톤_결과 = [
    [24],
    [21.6]
    ...
]

// 1. 과거의 데이터를 준비합니다.
var 원인 = tf.tensor(보스톤_원인);
var 결과 = tf.tensor(보스톤_결과);

// 2. 모델의 모양을 만듭니다. 
var X = tf.input({ shape: [13] });
var Y = tf.layers.dense({ units: 1 }).apply(X);
var model = tf.model({ inputs: X, outputs: Y });
var compileParam = { optimizer: tf.train.adam(), loss: tf.losses.meanSquaredError }
model.compile(compileParam);

// 3. 데이터로 모델을 학습시킵니다. 
// var fitParam = { epochs: 100 }
var fitParam = {
    epochs: 100,
    callbacks: {
        onEpochEnd: function (epoch, logs) {
            console.log('epoch', epoch, logs, "RMSE --> ", Math.sqrt(logs.loss));
        }
    }
} // loss 추가 예제
model.fit(원인, 결과, fitParam).then(function (result) {
    // 4. 모델을 이용합니다. 
    // 4.1 기존의 데이터를 이용
    var 예측한결과 = model.predict(원인);
    예측한결과.print();
});

그런데 만약 종속 변수가 복수개라면 어떻게 학습시켜야 할까?

만약 집 값 뿐만 아니라 하위계층 비율도 다른 컬럼으로부터 영향을 받는 종속 변수라면?

아래의 표를 보자.

get-price-of-houses-in-boston-2

10.4.html

var 보스톤_원인 = [
    [0.00632,18,2.31,0,0.538,6.575,65.2,4.09,1,296,15.3,396.9],
    [0.02731,0,7.07,0,0.469,6.421,78.9,4.9671,2,242,17.8,396.9]
    ...
]
var 보스톤_결과 = [
    [4.98,24],
    [9.14,21.6]
    ...
]

var 원인 = tf.tensor(보스톤_원인);
var 결과 = tf.tensor(보스톤_결과);

// 2. 모델의 모양을 만듭니다. 
var X = tf.input({ shape: [12] });              //change from 13 to 12
var Y = tf.layers.dense({ units: 2 }).apply(X); //change from 1 to 2
var model = tf.model({ inputs: X, outputs: Y });
var compileParam = { optimizer: tf.train.adam(), loss: tf.losses.meanSquaredError }
model.compile(compileParam);
...

바뀐 종속 변수와 독립 변수의 갯수에 따라 input, output 컬럼 수를 조정한 후에 학습시키면 된다.

weight와 bias 값을 구하면 아래와 같이 array가 확인되는 것을 볼 수 있다. array의 첫번 째 값은 하위계층비율 컬럼을 위한 가중치와 편향이며, 두번째는 값은 집 값 컬럼을 위한 가중치와 편향을 나타낸다. 

weight-bias-for-price

이 결과를 가지고 하위계층비율을 위한 공식을 만들어보면 아래와 같은 식이 도출된다. 이와 같이 어렵고 복잡한 공식도 TensorFlow.js를 이용하면 쉽게 구할 수 있다.

0.6092808842658997CRIM0.22322581708431244ZN+0.16122820973396INDUS0.6106813549995422CHAS0.16722729802131653NOX0.15347926318645477RM+0.24729061126708984AGE0.6148989796638489DIS+0.6361815333366394RAD0.06893382221460342TAX0.1256694793701172PTRATIO+0.06380190700292587B0.3050856888294220.6092808842658997*CRIM -0.22322581708431244*ZN +0.16122820973396*INDUS -0.6106813549995422*CHAS -0.16722729802131653*NOX -0.15347926318645477*RM +0.24729061126708984*AGE -0.6148989796638489*DIS +0.6361815333366394*RAD -0.06893382221460342*TAX -0.1256694793701172*PTRATIO +0.06380190700292587*B -0.305085688829422

학습을 얼마나 해야 하는가?

학습을 하다보면 얼마나 학습을 해야 하는가가 애매해지는 경우가 있다. 그럴 때는 RMSE나 loss 값을 참고하면 되지만 loss 값의 추세를 알면 어느정도까지 학습을 해야하는지 더 분명하게 결정할 수 있다.

TensorFlow.js Vis를 이용하면 모델링 할 때 얼마나 학습이 잘 되었는지를 시각적으로 확인할 수 있다.

tensorflow/tfjs-vis github

...
<head>
    <title>TensorFlow.js Tutorial - lemon</title>

    <!-- Import TensorFlow.js -->
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@1.0.0/dist/tf.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs-vis"></script>
    <script src="10.3.js"></script>
</head>
...

몇가지 함수들을 살펴보자.

  • tfvis.show.modelSummary

    만들어진 모델을 요약해서 보여주는 기능이다.

    tfvis.show.modelSummary({name:'요약', tab:'모델'}, model)

    tfjs-vis

  • tfvis.show.history

    만들어진 모델의 loss 값을 그래프로 보여준다.

    tfvis.show.history(
    	{name:'loss', tab:'역사'},
    	[{loss:10}, {loss:5}, {loss:1}], //역사 데이터를 담고 있는 배열
    	['loss']
    )

    tfjs-vis-2

    이 함수를 이용하면 실시간으로 학습되는 과정을 그래프로 확인할 수 있다.

    11.html
    ...
    // 3. 데이터로 모델을 학습시킵니다. 
    var _history = [];
    var fitParam = {
        epochs: 100,
        callbacks: {
            onEpochEnd: function (epoch, logs) {
                console.log('epoch', epoch, logs, "RMSE --> ", Math.sqrt(logs.loss));
                _history.push(logs); //loss 프로퍼티를 담는다.
                tfvis.show.history(
                    { name: 'loss', tab: '역사' },
                    _history,
                    ['loss']
                )
            }
        }
    }
    ...

    Perceptron

    인공 지능 분야에서 학습을 시킨다는 것은 최적의 가중치를 찾아내는 과정이라고 할 수 있다. 아래의 그림은 인간의 신경 세포를 모방하면 인공 지능을 만들 수 있지 않을까 라는 생각에서 시작된 것으로 인간의 뇌를 이루고 있는 neuron을 모방한 것인데 이것을 perceptron이라고 한다.

    percetron

    복수개의 독립 변수와 종속 변수가 존재한다면 perceptron은 여러개의 병렬로 이루어질 것이다.

    뇌의 neuron처럼 perceptron이 연결된다면 어떨까 라는 발상에서 시작된 것을 Artificial Neural Network, 인공신경망이라고 한다. ANN의 핵심은 perceptron을 넓고 깊게 연결하는 것인데 이를 이용해 복잡한 문제를 해결하는 것을 Deep Learning 이라고 한다.

    ann

    • node: 하나의 값

    • layer: node 들의 그룹
      - input layer: model 로 입력을 받는 layer
      - output layer: model 로 연산된 값을 출력하는 layer
      - hidden layer: model 밖에서는 보이지 않는 layer

      hidden layer가 많을수록 더 좋은 결과를 얻을 수 있으나 그만큼 많은 시간이 소요되므로 적당한 규모로 사용하는 것이 중요하다.

      Hidden Layer 추가하기

      Hidden Layer를 추가하면 조금 더 Deep Learning 다운 코드로 학습시킬 수 있다. 일반적으로 Hidden Layer의 node는 Input layer와 Output Layer 사이 값을 입력하여 시작하며 테스트를 통해 적당한 값으로 조정해나간다.

      12.2.html
      
      ...
      // 2. 모델의 모양을 만듭니다. 
      var X = tf.input({ shape: [13] });
      var H = tf.layers.dense({ units: 13 }).apply(X); //Hidden Layer 추가
      var Y = tf.layers.dense({ units: 1 }).apply(H);
      ...

      Hidden Layer에는 Activation Function이라는 것을 이용하여 좀 더 좋은 결과를 얻을 수 있다.

      var H = tf.layers.dense({ units: 13, activation:'relu' }).apply(X); //general

      그렇다면 Hidden Layer는 몇 개가 적당할까? 어떤 문제는 Hidden Layer 1개로도 해결이 되는 경우도 있다. 하지만 어떤 문제는 매우 복잡하여 수많은 학습과 여러가지 테스트를 통해 좋은 결과를 만들어낼 수 있는 구성을 찾아내야 한다.

참고

JS로 만드는 AI : TensorFlow.js - 1. 수업소개

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

0개의 댓글