Нейросети на JavaScript

Если Вы поищите по npm репозиторию фразу "machine learning", то получите пачку готовых библиотек, одна из них synaptic. Давайте её немного изучим.

Для начала давайте сделаем простейший пример, двоичный сумматор.

Устанавливаем библиотеку:

npm install synaptic --save

Создаём и открываем файл: example.js

Подключаем библиотеку:

const synaptic = require('synaptic')
const Layer = synaptic.Layer
const Network = synaptic.Network

Создаём слои для нейросети:

var inputLayer = new Layer(2)
var hiddenLayer = new Layer(3)
var outputLayer = new Layer(2)

Связываем слои и объединяем в сеть:

inputLayer.project(hiddenLayer)
hiddenLayer.project(outputLayer)

var myNetwork = new Network({
  input: inputLayer,
  hidden: [hiddenLayer],
  output: outputLayer
})

Тренируем сеть, один пример закомменчен специально, до этого варианта сеть должна додуматься сама:

var learningRate = .1
for(let i=0; i<500; i++){
  myNetwork.activate([0,0])
  myNetwork.propagate(learningRate, [0,0])
  /*
  myNetwork.activate([0,1])
  myNetwork.propagate(learningRate, [0,1])
  */
  myNetwork.activate([1,0])
  myNetwork.propagate(learningRate, [0,1])
  
  myNetwork.activate([1,1])
  myNetwork.propagate(learningRate, [1,1])
}

Выводим результат в консоль:

console.log('Result_1 for [1,1]:', myNetwork.activate([1,1]))
console.log('****')
console.log('Unknown for [0,1]:', myNetwork.activate([0,1]))

Запускаем:

node example.js

Результат:

*** EXAMPLE ***
Result_1 for [1,1]: [ 0.9106668845141498, 0.9975732147225783 ]
****
Unknown for [0,1]: [ 0.4636098279080098, 0.8874390079087119 ]

Для известного примера результат идеальный, неизвестный пример тоже задетектился.

Ок, давайте сделаем что-нить поинтересней. Например, предскажем поведение функции синуса.

Пишем функцию, которая будет выдавать данные для обучения и проверки:

function getRandomRange(min, max) {
  return Math.random() * (max - min) + min
}

function degreeToRad(degrees) {
  return (degrees * (Math.PI/180))
}

function getData() {
  const step = 15
  let start = getRandomRange(0, 359)
  let data = []
  let teachData = []
  let value, tmpValue
  
  for(let i=0; i<10; i++) {
    value = ( (Math.sin(degreeToRad(start)) + 1)/2 ).toFixed(2) * 1
    data.push(value)
    start += step
  }
  
  for(i=0; i<2; i++) {
    tmpValue = ( (Math.sin(degreeToRad(start)) + 1)/2 ).toFixed(2) * 1
    if( (tmpValue - value) > 0 ) {
      teachData.push(1)
      teachData.push(0)
    } else if( (tmpValue - value) < 0 ) {
      teachData.push(0)
      teachData.push(1)
    } else {
      teachData.push(0)
      teachData.push(0)
    }
    value = tmpValue
    start += start
  }
  
  return {data: data, teachData: teachData}
}

Подробнее.

На вход нам нужен массив со значениями, но т.к нейроны принимают значения ТОЛЬКО от 0 до 1, нам нужно нормализовать данные, по этому делаем:

value = ( (Math.sin(degreeToRad(start)) + 1)/2 ).toFixed(2) * 1

.toFixed возвращает строку, а не число, и чтобы получилось число, результат умножаем на 1

И на выходе, пусть наши нейроны предсказывают не точную цифру, а направление движения функции, по этому используем массив вида:

[
  0: X, - будет рост на первом шаге
  1: X, - будет падение на первом шаге
  2: X, - будет рост на втором шаге
  3: X, - будет падение на втором шаге
]

Где X может принимать значение только 0 или 1, опять же потому что нейроны могут выдавать значения только в диапазоне от 0 до 1.

Создаём нейросеть:

let inputLayer = new Layer(10)
let hiddenLayer = new Layer(15)
let outputLayer = new Layer(4)

inputLayer.project(hiddenLayer)
hiddenLayer.project(outputLayer)

let myNetwork = new Network({
  input: inputLayer,
  hidden: [hiddenLayer],
  output: outputLayer
})

Количество нейронов в первом слое = длинна массива входных данных. Количество нейронов в скрытом слое - установил случайно, можете поэкспериментировать с этим. И количество нейронов в выходном слое = длинна результирующего массива.

Обучаем нейросеть:

let learningRate = .1
for(let i=0; i<10000; i++){
  learnData = getData()
  myNetwork.activate(learnData.data)
  myNetwork.propagate(learningRate, learnData.teachData)
}

В этом примере уже требуется большее количество уроков.

И выводим результат:

learnData = getData()

console.log('learnData', learnData)
console.log('result', myNetwork.activate(learnData.data))

Получаем:

learnData { data: [ 0.02, 0, 0.01, 0.05, 0.13, 0.23, 0.35, 0.47, 0.6, 0.73 ],
  teachData: [ 1, 0, 1, 0 ] }
result 
[ 0.9999946502266344,
  0.0000017269654177571157,
  0.9924633941856511,
  0.008608194712312462 
]

Что точно соответствует ожидаемому результату. Теперь осталось немного модифицировать сеть, подключить АПИ с биржи мы сказочно богаты!



Пример использования Google-vision API

Появилась задача распознать текст на пачке картинок (~30k штук), при этом tesseract спасовал, выдавая очень нестабильный результат. По этому принял решение использовать всю мощь корпорации добра.

Продолжаем эксперименты с нейросетями

В этот раз постепенно усложним задачу. К прошлому примеру добавим новую кривую с другим периодом. Это разнообразит ситуацию


(0) Комментариев