오픈튜토리얼스 8.13 ~ 8.26  머신러닝야학에서 배운

레몬에이드 판매량 예측, 보스턴 집값 예측, 붓꽃 분류를 tensorflow.js로 만들었습니다.

오픈튜토리얼스 머신러닝야학 내용은 아래 링크를 참조해 주세요!

https://opentutorials.org/course/4570

 

Tensorflow 1 - 생활코딩

수업소개 이 수업은 코드로 딥러닝을 구현해보는 딥러닝 기초 수업입니다.  텐서플로우를 이용하여 가장 간단한 형태의 텐서플로우 딥러닝 모델을 작성합니다. 무엇을 넣을까가 아니라, 무엇

opentutorials.org

nodejs version: 12.18.3

tensorflow.js version: 2.3.0

 

전체 코드

  // csv 파일 읽기
  const irisCSVPath = 'file://' + path.join(__dirname, '../data/iris.csv')
  const irisCSV = tf.data.csv(irisCSVPath, {
    hasHeader: true,
    columnConfigs: { '품종': { isLabel: true } }
  })

  // 데이터셋 만들기
  const irisDataset = irisCSV.map(({ xs, ys }) => {
    let label = null
    switch (ys['품종']) {
      case 'setosa':
        label = [1, 0, 0]
        break;
      case 'versicolor':
        label = [0, 1, 0]
        break;
      case 'virginica':
        label = [0, 0, 1]
        break;
      default:
        break;
    }
    return { xs: Object.values(xs), ys: label }
  }).batch(16).shuffle(10)

  // 모델 만들기
  const input = tf.input({ shape: [4] })

  let hidden = tf.layers.dense({ units: 8 }).apply(input)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  hidden = tf.layers.dense({ units: 8 }).apply(hidden)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  hidden = tf.layers.dense({ units: 8 }).apply(hidden)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  const output = tf.layers.activation({ activation: 'softmax' }).apply(tf.layers.dense({ units: 3 }).apply(hidden))

  const model = tf.model({ inputs: input, outputs: output })
  model.compile({ optimizer: tf.train.adam(0.001), loss: 'categoricalCrossentropy', metrics: 'accuracy' })
  model.summary()

  // 모델 학습
  await model.fitDataset(irisDataset, {
    epochs: 1000
  })

  await model.fitDataset(irisDataset, {
    epochs: 10,
    callbacks: {
      onEpochEnd: async (epoch, logs) => {
        console.log(`epoch${epoch}: loss= ${logs.loss}, acc= ${logs.acc}`);
      }
    }
  })

  // 테스트 데이터로 분류 결과값 확인
  const testDataset = await createTestDataSet(irisDataset, 0, 0, 8)
  model.predict(testDataset.xs).print()
  testDataset.ys.print()
  model.getWeights()[0].print()

CSV 파일 읽기

iris csv 파일을 읽어 오면서 header를 가지고 있고 라벨이 품종이라는 점을 표시합니다.

  const irisCSVPath = 'file://' + path.join(__dirname, '../data/iris.csv')
  const irisCSV = tf.data.csv(irisCSVPath, {
    hasHeader: true,
    columnConfigs: { '품종': { isLabel: true } }
  })

데이터셋 만들기

python tensorflow에서는 get_dummies 함수를 사용하면 품종 항목이 나누어집니다.

하지만 tensorflow.js에서 찾을 수가 없었습니다.

ys는 라벨을 표시한 값이 객체 형식으로 담겨져 있어 '품종'이 무엇인지 확인하고 

switch 문을 사용해 값을 변경하여 줍니다.

const irisDataset = irisCSV.map(({ xs, ys }) => {
    let label = null
    switch (ys['품종']) {
      case 'setosa':
        label = [1, 0, 0]
        break;
      case 'versicolor':
        label = [0, 1, 0]
        break;
      case 'virginica':
        label = [0, 0, 1]
        break;
      default:
        break;
    }
    return { xs: Object.values(xs), ys: label }
  }).batch(16).shuffle(10)

 


모델 만들기

보스턴 집값 예측과 같이 layerNormalization을 사용하였습니다.

다른 점은 활성화 함수가 마지막 레이어에서 분류에 적합한 softmax를 사용합니다.

학습률은 0.001이고 모델 학습을 하면서 정확도를 파악하기 위해 metrics 값으로 'accuracy'를 추가하였습니다. 

const input = tf.input({ shape: [4] })

  let hidden = tf.layers.dense({ units: 8 }).apply(input)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  hidden = tf.layers.dense({ units: 8 }).apply(hidden)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  hidden = tf.layers.dense({ units: 8 }).apply(hidden)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  const output = tf.layers.activation({ activation: 'softmax' }).apply(tf.layers.dense({ units: 3 }).apply(hidden))

  const model = tf.model({ inputs: input, outputs: output })
  model.compile({ optimizer: tf.train.adam(0.001), loss: 'categoricalCrossentropy', metrics: 'accuracy' })
  model.summary()

모델 학습과 데이터셋 분류

기존 데이터셋에서 데이터를 가져와 확인 작업을 합니다.

getweights()[0] 은 첫번째 레이어가 가지는 weights 값들입니다.

  // 모델 학습
  await model.fitDataset(irisDataset, {
    epochs: 1000
  })

  await model.fitDataset(irisDataset, {
    epochs: 10,
    callbacks: {
      onEpochEnd: async (epoch, logs) => {
        console.log(`epoch${epoch}: loss= ${logs.loss}, acc= ${logs.acc}`);
      }
    }
  })

  // 테스트 데이터로 분류 결과값 확인
  const testDataset = await createTestDataSet(irisDataset, 0, 0, 8)
  model.predict(testDataset.xs).print()
  testDataset.ys.print()
  model.getWeights()[0].print()

결과값과 실제값 비교: 

Tensor
    [[0.0000133, 0.999977 , 0.0000097],
     [4e-7     , 0.9999844, 0.0000151],
     [0.0176175, 0.9823747, 0.0000078],
     [0.0000022, 0.9999913, 0.0000065],
     [0.0001166, 0.0010784, 0.998805 ],
     [0.0000383, 0.0020841, 0.9978777],
     [0.0000362, 0.002025 , 0.9979388],
     [0.0000181, 0.0106683, 0.9893135]]
Tensor
    [[0, 1, 0],
     [0, 1, 0],
     [0, 1, 0],
     [0, 1, 0],
     [0, 0, 1],
     [0, 0, 1],
     [0, 0, 1],
     [0, 0, 1]]

 

https://js.tensorflow.org/api/2.3.0/

 

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

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

www.tensorflow.org

 

'머신러닝' 카테고리의 다른 글

tensorflow.js 2) 보스턴 집값 예측  (0) 2020.08.27
tensorflow.js 1) 레몬에이드 판매량 예측  (0) 2020.08.27

오픈튜토리얼스 8.13 ~ 8.26  머신러닝야학에서 배운

레몬에이드 판매량 예측, 보스턴 집값 예측, 붓꽃 분류를 tensorflow.js로 만들었습니다.

오픈튜토리얼스 머신러닝야학 내용은 아래 링크를 참조해 주세요!

https://opentutorials.org/course/4570

 

Tensorflow 1 - 생활코딩

수업소개 이 수업은 코드로 딥러닝을 구현해보는 딥러닝 기초 수업입니다.  텐서플로우를 이용하여 가장 간단한 형태의 텐서플로우 딥러닝 모델을 작성합니다. 무엇을 넣을까가 아니라, 무엇

opentutorials.org

nodejs version: 12.18.3

tensorflow.js version: 2.3.0

 

전체 코드

  const bostonPriceCSV = tf.data.csv(
    'file://' + path.join(__dirname, '../data/boston.csv'),
    { columnConfigs: { medv: { isLabel: true } } }
  )

  // tf.data.Dataset 으로 변환
  const bostonPriceDataset = bostonPriceCSV.map(({ xs, ys }) => {
    return { xs: Object.values(xs), ys: Object.values(ys) }
  }).batch(32).shuffle(10)

  // 모델 생성
  const input = tf.input({ shape: [13] })

  let hidden = tf.layers.dense({ units: 8 }).apply(input)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  hidden = tf.layers.dense({ units: 8 }).apply(hidden)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  hidden = tf.layers.dense({ units: 8 }).apply(hidden)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  const output = tf.layers.dense({ units: 1 }).apply(hidden)

  const model = tf.model({
    inputs: input,
    outputs: output
  })
  model.compile({ optimizer: tf.train.adam(0.001), loss: tf.losses.meanSquaredError })
  model.summary()

  // 모델 학습
  await model.fitDataset(bostonPriceDataset, {
    epochs: 1000
  })

  await model.fitDataset(bostonPriceDataset, {
    epochs: 10,
    callbacks: {
      onEpochEnd: async (epoch, logs) => {
        console.log(epoch + ':' + logs.loss);
      }
    }
  })

  // 테스트 데이터 생성
  const testDataset = await createTestDataSet(bostonPriceDataset, 0, 0, 5)

  // 보스터 집값 예측
  model.predict(testDataset.xs).print()
  testDataset.ys.print()
  model.getWeights()[0].print()

 


CSV 파일 읽어오기

레몬에이드 판매량 예측에서는 tf.data.csv 함수를 사용하지 않고 readCSV 함수를 만들어 사용했습니다.

tf.data.csv 함수는

'file://' + path.join(__dirname'../data/boston.csv') 같이 'file://'를 꼭 붙여 주어야 합니다..

path.join 안에 "file://" 을 넣으면 에러가 발생합니다..

참고로 tensorflow.js API Reference 에서 tf.data.csv 함수는 

https://storage.googleapis.com/tfjs-examples/multivariate-linear-regression/data/boston-housing-train.csv

와 같은 형태의 URI 절대 주소를 사용합니다.

 

columnConfigs 부분은 medv 열이 종속변수임을 알려줍니다. 

  const bostonPriceCSV = tf.data.csv(
    'file://' + path.join(__dirname, '../data/boston.csv'),
    { columnConfigs: { medv: { isLabel: true } } }
  )

  // tf.data.Dataset 으로 변환
  const bostonPriceDataset = bostonPriceCSV.map(({ xs, ys }) => {
    return { xs: Object.values(xs), ys: Object.values(ys) }
  }).batch(32).shuffle(10)

모델 학습을 위해 tf.data.Dataset으로 변환합니다. 배치 사이즈는 32이고 셔플 시드는 10으로 지정하였습니다.

batch와 shuffle을 한 이유는 학습 속도를 향상 시키기 위해서 입니다. 자세한 부분은 더 공부해야 합니다...ㅠㅠ

 


모델 만들기

한 히든 레이어를 3개로 나누었습니다. 활성화 함수는 swish를 사용하려 하였으나

tensorflow.js에 없어서 대신 selu를 사용하였습니다. 

BatchNormalization을 처음에 사용하였는데 데이터 크기가 작아서인지 정확도가 많이 떨어졌습니다.

그래서 LayerNormalization을 사용했는데 정확도가 향상되었습니다.

이 두 정규화도 차이점이 무엇인지 공부해야 합니다.

// 모델 생성
  const input = tf.input({ shape: [13] })

  let hidden = tf.layers.dense({ units: 8 }).apply(input)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  hidden = tf.layers.dense({ units: 8 }).apply(hidden)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  hidden = tf.layers.dense({ units: 8 }).apply(hidden)
  hidden = tf.layers.layerNormalization().apply(hidden)
  hidden = tf.layers.activation({ activation: 'selu' }).apply(hidden)

  const output = tf.layers.dense({ units: 1 }).apply(hidden)

  const model = tf.model({
    inputs: input,
    outputs: output
  })
  model.compile({ optimizer: tf.train.adam(0.001), loss: tf.losses.meanSquaredError })
  model.summary()

optimizer는 adam으로 학습률은 0.001로 하였습니다. 손실함수는 평균 제곱근 편차를 사용했습니다.

 


모델 학습

여기서 파이썬에서 model.fit()을 사용하면 epoch 를 몇 번 학습시켰는지 알 수 있습니다.

하지만 자바스크립트에서는 콜백함수 onEpochEnd를 설정해야

한 epoch가 끝나고 epoch 순서와 손실을 알 수 있습니다.

여기서 tfjs-vis API를 프론트엔드에서 사용하면 실시간으로 진행 상황을 확인할 수 있습니다.

  // 모델 학습
  await model.fitDataset(bostonPriceDataset, {
    epochs: 1000
  })

  await model.fitDataset(bostonPriceDataset, {
    epochs: 10,
    callbacks: {
      onEpochEnd: async (epoch, logs) => {
        console.log(epoch + ':' + logs.loss);
      }
    }
  })

테스트 데이터 생성

따로 테스트 데이터셋을 준비해야하지만 기존 데이터셋에서 테스트 데이터를 생성하여 사용합니다...

// 테스트 데이터 생성
  const testDataset = await createTestDataSet(bostonPriceDataset, 0, 0, 5)

createTestDataSet은 기존 데이터셋에서 batchNumber와 범위를 정해 tensor들을 추출합니다.

tf.tidy 함수에서 생성된 tf는 함수 종료 후 없어져 메모리 활용도를 높입니다.

tf.gather 함수는 2차원 tensor에서 1차원 tensor들을 추출하는데 사용됩니다.

/**
   * features와 label을 tensor2d로 반환합니다.
   * @param {tf.data.Dataset} DataSet 
   * @param {Integer} batchNumber 
   * @param {Integer} start  
   * @param {Integer} end 
   */
const createTestDataSet = async (DataSet, batchNumber, start, end) => {
  if (start > end) {
    throw new Error("Error: 'start' is bigger than 'end'")
  }
  if (start < 0 || end < 0) {
    throw new Error("Error: 'start' and 'end' start from zero")
  }

  const dataArr = await DataSet.toArrayForTest()

  if (dataArr.length < batchNumber) {
    throw new Error("Error: Exceed batch number")
  }

  const batchSize = dataArr[batchNumber].xs.shape[0]
  if (end > batchSize) {
    throw new Error('Error: Exceed batch size')
  }

  let range = []
  for (let i = start; i < end; i++) {
    range.push(i)
  }
  const tensor1dRange = tf.tensor1d(range, 'int32')

  const xs = tf.tidy(() => {
    const tensor2dXs = dataArr[batchNumber].xs
    const XsTestDataset = tf.gather(tensor2dXs, tensor1dRange)
    return XsTestDataset
  })

  const ys = tf.tidy(() => {
    const tensor2dYs = dataArr[batchNumber].ys
    const YsTestDataset = tf.gather(tensor2dYs, tensor1dRange)
    return YsTestDataset
  })
  return { xs, ys }
}

보스턴 집값 예측

getweights()[0] 은 첫번째 레이어가 가지는 weights 값들입니다.

 

  // 보스터 집값 예측
  model.predict(testDataset.xs).print()
  testDataset.ys.print()
  model.getWeights()[0].print()

참조

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

 

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

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

www.tensorflow.org

공부할 점

1. batchNormalization, layerNormalization 차이점 알기

2. tfjs.vis 활용법 생각하기

'머신러닝' 카테고리의 다른 글

tensorflow.js 3) 붓꽃 분류하기  (0) 2020.08.28
tensorflow.js 1) 레몬에이드 판매량 예측  (0) 2020.08.27

오픈튜토리얼스 8.13 ~ 8.26  머신러닝야학에서 배운

레몬에이드 판매량 예측, 보스턴 집값 예측, 붓꽃 분류를 tensorflow.js로 만들었습니다.

오픈튜토리얼스 머신러닝야학 내용은 아래 링크를 참조해 주세요!

https://opentutorials.org/course/4570

 

Tensorflow 1 - 생활코딩

수업소개 이 수업은 코드로 딥러닝을 구현해보는 딥러닝 기초 수업입니다.  텐서플로우를 이용하여 가장 간단한 형태의 텐서플로우 딥러닝 모델을 작성합니다. 무엇을 넣을까가 아니라, 무엇

opentutorials.org

nodejs version: 12.18.3

tensorflow.js version: 2.3.0

전체 코드

  const lemonadeCSV = await readCSV('./data/lemonade.csv')

  // 데이터를 tensor 형태로 만들기
  const temperature = []
  const sales = []
  for (let i = 0; i < lemonadeCSV.length; i++) {
    temperature.push(Number(lemonadeCSV[i]['온도']))
    sales.push(Number(lemonadeCSV[i]['판매량']))
  }

  const independent = tf.tensor1d(temperature)
  const dependent = tf.tensor1d(sales)

  independent.print()
  dependent.print()

  // 모델 만들기
  const input = tf.input({ shape: [1] })
  const output = tf.layers.dense({ units: 1 }).apply(input)
  const model = tf.model({
    inputs: input,
    outputs: output
  })

  model.compile({ optimizer: tf.train.adam(), loss: tf.losses.meanSquaredError })
  model.summary()

  // 모델 학습
  const { history: { loss: loss } } = await model.fit(independent, dependent, { epochs: 15000 })

  // 마지막 loss 출력
  console.log(loss[loss.length - 1])

  // 판매량 예측
  model.predict(tf.tensor1d([40])).print()

CSV 데이터 가져오기

tf.data.csv() 함수가 작동하지 않아 csv를 읽어오는 함수를 만들게 되었습니다. ㅠㅠ

node.js fs 모듈과 csv-parser npm 모듈을 사용하였습니다.

ReadStream을 사용하여 csv 파일을 읽고 객체 배열 형태로 값을 반환합니다.

const fs = require('fs')
const csv = require('csv-parser')

/**
 * 
 * @param { string } csvFilePath csv 파일 주소
 * 리턴값은 [{'a':'1', 'b':'2'}] 객체 배열 형태입니다.
 */
const readCSV = (csvFilePath) => {
  return new Promise((resolve, reject) => {
    const dataSet = []
    const readStream = fs.createReadStream(csvFilePath)
    readStream.pipe(csv()).on('error', () => { return reject(new Error('Error reading file')) }).on('data', (data) => { dataSet.push(data) }).on('end', () => { resolve(dataSet) })
  })
}

module.exports = readCSV

데이터를 Tensor 형으로 만들기

  const temperature = []
  const sales = []
  for (let i = 0; i < lemonadeCSV.length; i++) {
    temperature.push(Number(lemonadeCSV[i]['온도']))
    sales.push(Number(lemonadeCSV[i]['판매량']))
  }

  const independent = tf.tensor1d(temperature)
  const dependent = tf.tensor1d(sales)
 
 /*
 Tensor
    [20, 21, 22, 23, 24, 25]
Tensor
    [40, 42, 44, 46, 48, 50]
*/

모델 만들기

tensorflow.js와 python tensorflow 다른점은 apply를 사용해 layer를 쌓고

parameter 값을 Object로 사용한다는 점입니다.  

  const input = tf.input({ shape: [1] })
  const output = tf.layers.dense({ units: 1 }).apply(input)
  const model = tf.model({
    inputs: input,
    outputs: output
  })

  model.compile({ optimizer: tf.train.adam(), loss: tf.losses.meanSquaredError })
  model.summary()

model Summary :

_________________________________________________________________
Layer (type)                 Output shape              Param #
========================================
input1 (InputLayer)          [null,1]                  0
_________________________________________________________________
dense_Dense1 (Dense)         [null,1]                  2
========================================
Total params: 2
Trainable params: 2
Non-trainable params: 0
_________________________________________________________________


모델 학습과 예측

 

model.fit()은 history를 반환하고 history에는 지금까지 loss 값을 가지고 있습니다.

결과: 

0.015542778186500072

Tensor 
     [[78.7350845],]

  // 모델 학습
  const { history: { loss: loss } } = await model.fit(independent, dependent, { epochs: 15000 })

  // 마지막 loss 출력
  console.log(loss[loss.length - 1])

  // 판매량 예측
  model.predict(tf.tensor1d([40])).print()

가장 간단한 lemonade 판매량 예측이었습니다. 하지만 python이 아니라 javascript로 하고 자료도 많지 않아서 tensorflow.js API reference 와 튜토리얼을 보면서 했습니다. 

tensorflow.js API reference 는 사용법이 자세히 나와있지 않아 구글링을 많이 하였습니다.

 

https://js.tensorflow.org/api/2.3.0/

 

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

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

www.tensorflow.org

 

'머신러닝' 카테고리의 다른 글

tensorflow.js 3) 붓꽃 분류하기  (0) 2020.08.28
tensorflow.js 2) 보스턴 집값 예측  (0) 2020.08.27

+ Recent posts