Merge Sort

병합정렬

시간 복잡도: O(nlogn)

공간 복잡도: O(n)

 

merge 함수는 두 배열을 합쳐 한 배열로 정렬합니다.

mergeSort 함수는 재귀적으로 배열을 나누고 merge 함수로 합칩니다.

/**
 * @param {int[]} leftArr 
 * @param {int[]} rightArr 
 */
const merge = (leftArr, rightArr) => {
  let result = []
  let leftIndex = 0
  let rightIndex = 0
  while (leftIndex < leftArr.length && rightIndex < rightArr.length) {
    if (leftArr[leftIndex] < rightArr[rightIndex]) {
      result.push(leftArr[leftIndex])
      leftIndex++
    } else {
      result.push(rightArr[rightIndex])
      rightIndex++
    }
  }
  while (leftIndex < leftArr.length) {
    result.push(leftArr[leftIndex])
    leftIndex++
  }
  while (rightIndex < rightArr.length) {
    result.push(rightArr[rightIndex])
    rightIndex++
  }
  return result
}

/**
 * 
 * @param {int[]} arr 
 */
const mergeSort = (arr) => {
  if (arr.length <= 1) return arr
  let mid = Math.floor(arr.length / 2)
  let left = mergeSort(arr.slice(0, mid))
  let right = mergeSort(arr.slice(mid))

  let result = merge(left, right)
  return result
}

 

자바스크립트를 공부하고 있습니다.

잘못된 정보와 추가 내용은 댓글에 남겨주세요!!ㅠㅠㅠㅠ

'Javascript' 카테고리의 다른 글

About Javascript  (0) 2020.07.26

버블 정렬

시간 복잡도: O(n2)

공간 복잡도: O(1)

const swap = (arr, index, nextIndex) => {
  [arr[index], arr[nextIndex]] = [arr[nextIndex], arr[index]]
}

const bubbleSort = (arr) => {
  for (let i = arr.length; i >= 0; i--) {
    let noSwap = true
    for (let j = 0; j < i; j++) {
      if (arr[j] > arr[j + 1]) {
        swap(arr, j, j + 1)
        noSwap = false
      }
    }
    if (noSwap) break
  }
  return arr
}

선택 정렬

시간 복잡도: O(n2)

공간 복잡도: O(1)

const swap = (arr, index, nextIndex) => {
  [arr[index], arr[nextIndex]] = [arr[nextIndex], arr[index]]
}

const selectionSort = (arr) => {
  for (let i = 0; i < arr.length - 1; i++) {
    let lowest = i
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[lowest] > arr[j]) {
        lowest = j
      }
    }
    if (i !== lowest) {
      swap(arr, i, lowest)
    }
  }
  return arr
}

삽입 정렬

시간 복잡도: O(n2)

공간 복잡도: O(1)

const insertionSort = (arr) => {
  for (let i = 1; i < arr.length; i++) {
    console.log(arr)
    let currentValue = arr[i]
    for (let j = i - 1; j >= 0 && arr[j] > currentValue; j--) {
      arr[j + 1] = arr[j]
      arr[j] = currentValue
    }
  }
  return arr
}

오픈튜토리얼스 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

expect().toBe() 함수 형태를 어떻게 만들기 고민하였습니다.

이 형태는 then, catch를 사용하는 Promise 형태가 아니기 때문입니다.

function expect() {
  const trial = Function('"use strict";return (' + arguments[0] + ')')();
  return {
    toBe: (answer) => {
      const isEuqual = trial === answer
      if (isEuqual) {
        return console.log(`결과가 동일합니다. expect: ${trial} result: ${answer}`)
      }
      return console.log(`결과가 다릅니다. expect: ${trial} result: ${answer}`)
    }
  }
}

function add(a, b) {
  return a + b
}
expect("2+5").toBe(7)
expect("2*5").toBe(10)
expect("2/5").toBe(0.3)
expect(add(2, 3)).toBe(5)
expect(2 + 5).toBe(7)

/**
 * 결과가 동일합니다. expect: 7 result: 7
 * 결과가 동일합니다. expect: 10 result: 10
 * 결과가 다릅니다. expect: 0.4 result: 0.3
 * 결과가 동일합니다. expect: 5 result: 5
 * 결과가 동일합니다. expect: 7 result: 7
 */

그래서 toBe 함수가 있는 객체를 반환하고자 하였습니다.

jest Expect 문서에서 toBe()는 '==='를 의미하여

isEqual boolean 변수에 trial과 answer 비교 결과를 담았습니다.

 

trial은 expect 문에서 받은 arguments 을 실행한 결과를 가집니다.

처음에는 eval() 함수를 사용하려 하였으나 인자로 받은 코드를 caller 권한으로 수행하는 보안상 위험한 함수이고

최신 JS 엔진에서 여러 코드 구조를 최적화하는 것과 달리 eval()은 JS 인터프리터를 사용해야 하기 때문에 다른 대안들보다 느리기 때문에 Function 으로 대체하였습니다

 

<참조>

MDN eval() 절대 사용하지 말 것!

https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/eval

Jest Expect API reference

https://jestjs.io/docs/en/expect

 

Javascript Engine

자바스크립트 엔진은 컴퓨터가 이해할 수 있도록 javascript 파일을 변환합니다.

V8 engine

  1. parse the code(Parser)
  2. form Abstract syntax tree(AST) https://astexplorer.net/
  3. interpreter(바로 bytecode를 만듬, bytecode는 기계어가 아님, bytecode는 바로 실행할 때 필요함)
  4. profiler (monitor, 어떻게 최적화할지 살펴 봄)
  5. compiler
  6. optimized code (run faster)

javscript가 interpreter 언어인지 compile 언어인지는 구현에 달려있다.

최적화 방법

최적화 방해 요소(사용 주의)
https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#3-managing-arguments

  1. eval()
  2. arguments
  3. for in
  4. with
  5. delete

V8 엔진 최적화 방법

  1. Hidden classes
  2. Inline caching

Inline caching

function findUser(user) {
  return `${user.firstName}, ${user.lastName}`;
}

const userData {
  firstName: "jinsu"
  lastName : "Sang"
}

findUser(userData)
findUser(userData) // 앞에서 실행한 결과를 가져옴

Optimizing dynamic Javascript with inline caches
https://github.com/sq/JSIL/wiki/Optimizing-dynamic-JavaScript-with-inline-caches

hidden classes

function Animal(x, y) {
  this.x = x;
  this.y = y;
}
const obj1 = new Animal(1, 2);
const obj2 = new Animal(3, 4);

// 새로운 속성 추가
obj1.a = 30;
obj1.b = 100;

obj2.b = 30;
obj2.a = 100;

// 속성 삭제
delete obj1.a;

obj1은 [x, y, a, b] 순서로 값을 가지고 obj2는 [x, y, b, a] 순서로 값을 가집니다. 자바스크립트는 동적 언어로 런타임 도중 값이 변할 수 있습니다. 그래서 히든 클래스를 사용해 이전 데이터 구조를 저장해 놓습니다. 히든 클래스 예시에서 히든 클래스는 obj1, obj2에 다른 속성을 부여하면서 분기합니다.

참조
https://meetup.toast.com/posts/78
https://richardartoul.github.io/jekyll/update/2015/04/26/hidden-classes.html

WebAssembly

웹 어셈블리는 표준 이진 실행 포맷입니다. 웹 어셈블리는 고성능 애플리케이션을 웹 페이지나 다른 환경에서도 실행될 수 있도록 하였다.

Call Stack, Memory Heap

const number = 610; // allocate memory for number
const string = 'text'; // allocate memory for a string
const human = {
  // allocate memory for an object and its values
  first: 'jinsu',
  last: 'jinsu',
};

// Heap Memory에 정렬되지 않은 형태로 저장합니다.

function subtractTwo(num) {
  return num - 2;
}

function calculate() {
  const sum = 4 + 5;
  return subtractTwo(sum);
}

calculate();

// call stack in memory
/*
2. subtractTwo
1. calcuate
*/

Stack Overflow

function inception() {
  inception();
}
inception();
// output: Uncaught RangeError: Maximum call stack size exceeded

Garbage Collection and Memory Leaks

가비지 콜렉션은 사용하고 쓸모없어진 메모리를 청소하는 역할을 합니다. 자바스크립트에서는 mark and sweep 알고리즘을 사용한다. 메모리 주소를 참조하고 있는 객체나 변수과 해당 객체나 변수를 참조하고 있는 객체나 변수를 mark하고 mark되지 않은 객체나 변수를 sweep 합니다.

// Memory Leaks example
let array = [];
for (let i = 3; i > 0; i++) {
  array.push(i--);
}
// 구글 크롬에서 실행시 오류가 발생합니다.

// Memory Leaks 전역변수
var a = 1;
var b = 2;
var c = 3;

// 이벤트 리스너 (페이지 종료시 이벤트 리스너를 삭제하지 않으면 이벤트 리스너를 위해 필요한 메모리를 페이지에 들어올 때마다 확보하게 됩니다.)
var element = document.getElementById('button');
element.addEventListener('click', onClick);

// setInterval
setInterval(() => {
  // setInterval가 주기적으로 실행되면서 계속 메모리를 추가 확보합니다.
});

싱글 스레드

싱글스레드는 한 명령 집합만을 한번에 실행한다는 것이다. call stack 하나를 소유해서 여러 작업을 동시에 하지 못하고 한번에 한 작업만을 수행합니다. 그러므로 자바스크립트는 동기적(Synchronous)으로 움직입니다.

javascript runtime

WebAPI는 웹이 기기 하드웨어나 데이터 저장소에 접근할 수 있도록 하는 기기 호환과 API모음을 나타내는 단어입니다. webAPI는 비동기적(Asynchronous)으로 움직입니다.
https://developer.mozilla.org/ko/docs/WebAPI

console.log('1');
setTimeout(() => {
  console.log('2');
}, 1000); // 0 ms 여도 결과는 같다!!
console.log('3');
// 1
// 3
// 2

자바스크립트 런타임 예시 사이트
http://latentflip.com/

자바스크립트 런타임 작동 방식을 보면 call stack에 작업이 동기적으로 쌓이지만 Web Apis 에게 수행하는 작업을 맡기고 call stack에 쌓인 작업들을 마무리합니다. Web Apis가 끝낸 작업은 Callback Queue에 들어가고 Call Stack이 비어있는 상태인지 확인하고 Call stack에 쌓이게 됩니다.

function a() {
  b();
}

function b() {
  c();
}

function c() {
  setTimeout(d, 5000);
  setTimeout(d, 5000);
  setTimeout(d, 5000);
}

function d() {
  console.log('Hi');
  setTimeout(e, 5000);
  setTimeout(e, 5000);
}

function e() {
  console.log('hello');
}

a();

Node.js

자바스크립트 엔진과 런타임은 다릅니다. 클래식에 비유하면 자바스크립트 코드는 악보, 엔진은 지휘자, 런타임은 지휘자, 악보, 악기 등을 포함한 악단이라고 생각하면 쉽습니다.
Node.js는 브라우저 자바스크립트 런타임과 비슷하지만 브라우저에서 사용하는 window가 아닌 global을 사용함으로서 브라우저를 벗어나 서버에서도 자바스크립트가 작동할 수 있도록 합니다.
Node.js는 non-blocking event driven asynchronous I/O 기반 서버 사이드 플랫폼입니다.

node.js 런타임 이해를 위한 자료

How does JavaScript work in the browser and node?
A visualization of JavaScript runtime, callback queue, and event loop, and Web APIs. Nothing major technical here. - Uday Hiwarale
https://medium.com/jspoint/how-javascript-works-in-browser-and-node-ab7d0d09ac2f

Philip Roberts: 어쨌든 이벤트 루프는 무엇입니까?
https://www.youtube.com/watch?time_continue=38&v=8aGhZQkoFbQ&feature=emb_title

Jake Archibald: 루프 속
https://www.youtube.com/watch?v=cCOL7MC4Pl0

'Javascript' 카테고리의 다른 글

javascript) this에 대해서  (0) 2020.09.13

+ Recent posts