Введение в машинное обучение прямо в браузере
[Пост также доступен на quaintitative.com]
Мне немного надоело возиться с Tensorflow и Keras в Python, и я решил попробовать применить машинное обучение с помощью tensorflow.js прямо в браузере. Это оказалось немного сложнее, чем я ожидал, в основном из-за необходимости адаптироваться к асинхронному характеру Javascript при загрузке данных. Но это было весело.
Данные и процесс оптимизации, визуализированные в D3 (мой собственный маленький TensorBoard, ура) можно увидеть здесь.
Это не анимация D3, мы фактически тренируем модель прямо в браузере на вашем компьютере! Мы обучаем простую модель линейной регрессии, используя пакетный градиентный спуск, оптимизатор Адама и среднеквадратичную ошибку в качестве функции потерь.
В файле data.js мы -
- Удаленная загрузка набора данных радужной оболочки (из сути)
- Разбивка их на заранее определенные размеры пакетов
А в файле regression2.js мы —
- Настройка необходимых переменных и констант тензорного потока
- Определение прогноза и функции потерь
- Обучение модели линейной регрессии с использованием загруженных данных и функций потерь/прогноза
- Построение графика рассеяния набора данных радужной оболочки
- График потерь при обучении по мере обучения модели прямо в вашем браузере!
- После завершения обучения сделайте ряд прогнозов и используйте эти точки для построения линии регрессии.
Код довольно длинный, и я добавил немало комментариев непосредственно в код, чтобы направлять читателя, поэтому я не буду рассматривать его шаг за шагом.
Позвольте мне выделить два ключевых блока кода.
Первый — это код, используемый в data.js для удаленной загрузки данных из gist.
async function fetchData(){
let response = await fetch(datasource);
let text = await response.text();
// Parse the text data into json with d3's function
let data = await d3.csvParse(text);
let categoricalTargets = ['setosa', 'versicolor', 'virginica'];
await data.forEach(d=>{
// Or do it with a separate categoricalTargets lookup
categoricalTargets.forEach((c,i)=>{
if (d.species==c){d.speciesNum=i};
})
x.push([+d.sepal_length]);
y.push(+d.speciesNum/1.0);
});
return {data:data, x:x, y:y};
}
Мы используем асинхронность и ожидаем здесь по очень простой причине. Это позволяет нам использовать новую функциональность Promise в Javascript ES6. Что здесь происходит, так это то, что каждая строка здесь с добавленным ожиданием фактически будет ждать завершения строки над ней, прежде чем она запустится.
Это в основном заменяет синтаксис типа function1().then(function2())
, к которому мы привыкли, и его намного легче читать и писать.
Второй блок — это основная часть кода, которую мы используем для обучения модели линейной регрессии.
// Setting up the variables for machine learning
const learning_rate = 0.05;
const batch_size = 25;
const A = tf.variable(tf.randomNormal(shape=[1,1]));
const b = tf.variable(tf.randomNormal(shape=[1,1]));
// Alternative way of initialising the two variables
// const A = tf.variable(tf.scalar(0.1));
// const b = tf.variable(tf.scalar(0.1));
const optimizer = tf.train.adam(learning_rate);
let lossArray = [];
function predict(xs){
return tf.tidy(()=>{
const ys = xs.mul(A).add(b);
return ys;
});
}
function loss(predict, actual){
return tf.tidy(()=>{
return predict.sub(tf.cast(actual, 'float32')).square().mean();
});
}
async function train(numIterations, done){
const d = await fetchData();
for (let i=0; i<numIterations; i++){
let cost;
const [xs,ys] = await batchData(d.x,d.y,50);
cost = tf.tidy(()=>{
cost = optimizer.minimize(()=>{
const pred = predict(xs);
const predLoss = loss(pred, ys);
return predLoss;
}, true);
return cost;
})
cost.data().then((data)=>lossArray.push({i:i, error:data[0]}));
if (i%100==0){
ploterrors(lossArray.slice(i-100,i));
await cost.data().then((data)=>console.log(i,data));
}
await tf.nextFrame();
}
done();
// If we want to check the values of A and b
// await A.data().then((data)=>console.log(data[0]));
// await b.data().then((data)=>console.log(data[0]));
}
Если вы знакомы с Tensorflow в Python, это должно быть довольно легко понять. Если нет, взгляните на документацию Tensorflow. Ключевое отличие здесь заключается в том, что мы не используем сеансы, а скорее объявляем прогнозирование, потери и т. д. как функции, которые затем вызываются позже в поезде с данными, которые будут использоваться для обучения.
Мы также добавляем await tf.nextFrame();
здесь. Это в основном гарантирует, что экран браузера продолжает отображаться даже во время обучения.
Также важно понимать, для чего нужен tf.tidy()
. Всякий раз, когда мы используем тензор, он занимает место в нашем GPU. Мы можем вызывать dispose
после каждого шага, чтобы очистить место, но это было бы очень утомительно. Что делает tf.tidy()
, так это то, что помогает нам очистить все промежуточные тензоры.
Есть еще кое-что, что меня озадачивает. В tensorflow.js мы можем выполнять операции с помощью tf.add(a,b)
или a.add(b)
. Но почему-то в рамках функции optimizer.minimize
работает только последний. Если кто знает почему, подскажите?
Полный код здесь.
playgrd.com || facebook.com/playgrdstar || instagram.com/playgrdstar/