從零開始的機器學習生活 – TensorFlow.js 學習顏色分類

從零開始的機器學習生活 – TensorFlow.js 學習顏色分類


在前面幾篇文章補足了一些基本知識之後,接著我們就可以進行一些基本的練習,本次做的是透過機器學習辨識顏色,下面將會附上程式碼以及 github page。如果你是 IOS 12 或是 Android 9 這些較新版本手機使用者,會有無法運算的 BUG 存在,只能靜待修復了。

資料

在寫程式之前,我們先談論關於資料來源這件事情,你可以自己收集資料、根據規則用程式產生資料(ex:衣服尺寸表)或是用 google 的 Dataset Search 本次的資料來源則用教學 CodingTrain 的顏色資料。

程式範例

先上 demo 以及 github 本次一起使用 P5.js 來實作,以下只貼出 js 的部分。

let data
let colors = []
let xs, ys
let model
let labels = []
let rSlider, gSlider, bSlider
let labelList = [
  '紅色系',
  '綠色系',
  '藍色系',
  '橘色系',
  '黃色系',
  '粉色系',
  '紫色系',
  '棕色系',
  '灰色系',
]


function preload() {
  data = loadJSON('color.json')
}

function setup() {
  let canvas = createCanvas(300, 300).parent('canvas')
  rSlider = createSlider(0, 255, 255).parent('sliderR')
  gSlider = createSlider(0, 255, 0).parent('sliderG')
  bSlider = createSlider(0, 255, 0).parent('sliderB')
  for (let record of data.entries) {
    let col = [record.r / 255, record.g / 255, record.b / 255] //Normalization
    colors.push(col)
    labels.push(labelList.indexOf(record.label))
  }
  xs = tf.tensor2d(colors)
  // console.log(xs.shape) //[row,col]

  let labelsTensor = tf.tensor1d(labels, 'int32')
  // labelsTensor.print()

  ys = tf.oneHot(labelsTensor, 9) // one hot encoding

  model = tf.sequential()

  let hiddenLayer = tf.layers.dense({
    units: 32, // 此隱藏層的神經元數量
    inputDim: 3, // R,G,B 三種 input
    activation: 'sigmoid', // 激活函數
  })
  let outputLayer = tf.layers.dense({
    units: 9, // 9 種 label
    activation: 'softmax',
  })

  model.add(hiddenLayer)
  model.add(outputLayer)

  const learnRate = 0.2
  const optimizer = tf.train.sgd(learnRate) // 梯度下降法

  model.compile({
    optimizer: optimizer,
    loss: 'categoricalCrossentropy' // 優化 loss 演算法 for 分類問題
  })

  train().then((res) => {
    console.log(res.history.loss)
  })
}

async function train() {

  const options = {
    epochs: 30, // 輪迴訓練次數
    validationSplit: 0.1, // 10% 資料驗證
    shuffle: true, // 亂數取樣
    callbacks: {
      // onTrainBegin: () => console.log('onTrainBegin'),
      // onTrainEnd: () => console.log('onTrainEnd'),
      // onEpochBegin: () => console.log('onEpochBegin'),
      onEpochEnd: (num, logs) => {
        document.getElementById("status").innerHTML = `epochs: ${num} 結束, Loss值: ${logs.loss}`
        // console.log(`epochs: ${num} 結束, Loss值: ${logs.loss}`)
        // console.log(logs)
      },
      // onBatchBegin: () => console.log('onBatchBegin'),
      onBatchEnd: () => {
        // console.log('onBatchEnd')
        return tf.nextFrame()
      }
    }
  }

  return await model.fit(xs, ys, options)
}

function draw() {
  let r = rSlider.value();
  let g = gSlider.value();
  let b = bSlider.value();
  background(r, g, b);
  strokeWeight(2);
  stroke(255);
  line(frameCount % width, 0, frameCount % width, height);

  // 防止記憶體被吃光
  tf.tidy(() => {
    const xs = tf.tensor2d([
      [r / 255, g / 255, b / 255]
    ])

    let result = model.predict(xs)
    let index = result.argMax(1).dataSync() //拿取機率最高的 index
    let label = labelList[index]

    document.getElementById("result").innerHTML= label 
    // console.log(label)
    // console.log(tf.memory().numTensors)
    // result.print()

  })


}

稍微講解一下程式架構,程式碼內也有註解。

  • 色碼進行 Normalization
  • label 進行 one hot encoding
  • 定義整個 model 結構
  • 使用 梯度下降法 尋找最優解
  • categoricalCrossentropy 優化 loss
  • 訓練參數
  • 預測結果輸出

上面幾行程式碼就可以寫出一個簡單的顏色預測功能,如果你不太了解某些地方為何要使用這些演算法可以看看 [資料分析&機器學習] 第2.4講:資料前處理(Missing data, One-hot encoding, Feature Scaling)

參考資料

损失函数 Losses
什么是激励函数 (Activation Function)