什么是Tensorflow.js?
TensorFlow.js是一個(gè)開(kāi)源的基于硬件加速的JavaScript庫(kù)奈附,用于訓(xùn)練和部署機(jī)器學(xué)習(xí)模型惭聂。谷歌推出的第一個(gè)基于TensorFlow的前端深度學(xué)習(xí)框架TensorFlow.js 是一個(gè)開(kāi)源的用于開(kāi)發(fā)機(jī)器學(xué)習(xí)項(xiàng)目的 WebGL-accelerated JavaScript 庫(kù)。TensorFlow.js可以提供高性能的盛泡、易于使用的機(jī)器學(xué)習(xí)構(gòu)建模塊捺癞,允許在瀏覽器上訓(xùn)練模型夷蚊,或以推斷模式運(yùn)行預(yù)訓(xùn)練的模型。TensorFlow.js 不僅可以提供低級(jí)的機(jī)器學(xué)習(xí)構(gòu)建模塊髓介,還可以提供高級(jí)的類似 Keras 的 API 來(lái)構(gòu)建神經(jīng)網(wǎng)絡(luò)惕鼓。
個(gè)人站點(diǎn):https://whl1207.github.io/,記錄一些學(xué)習(xí)筆記唐础,主要寫點(diǎn)基于anylogic的仿真和web開(kāi)發(fā)方面的知識(shí)點(diǎn)呜笑。有興趣的同學(xué)可以探討一下!
Tensorflow.js的優(yōu)點(diǎn)
- 1彻犁、不用安裝驅(qū)動(dòng)器和軟件叫胁,通過(guò)鏈接即可分享程序。
- 2汞幢、網(wǎng)頁(yè)應(yīng)用交互性更強(qiáng)驼鹅。
- 3、有訪問(wèn)GPS森篷,Camera输钩,Microphone,Accelerator仲智,Gyroscope等傳感器的標(biāo)準(zhǔn)api(主要是指手機(jī)端)买乃。
- 4、安全性钓辆,因?yàn)閿?shù)據(jù)都是保存在客戶端的剪验。
TensorFlow.js的應(yīng)用方式
1、在瀏覽器中開(kāi)發(fā)ML前联。使用簡(jiǎn)單直觀的API從頭構(gòu)建模型功戚,然后使用低級(jí)別的JavaScript線性代數(shù)庫(kù)或高層API進(jìn)行訓(xùn)練。
2似嗤、運(yùn)行現(xiàn)有模型啸臀。使用TensorFlow.js模型轉(zhuǎn)換器在瀏覽器中運(yùn)行預(yù)訓(xùn)練好的TensorFlow模型。
3烁落、重新訓(xùn)練現(xiàn)有模型乘粒。使用連接到瀏覽器的傳感器數(shù)據(jù)或其他客戶端數(shù)據(jù)重新訓(xùn)練ML模型。
一伤塌、基本概念
張量(Tensor)和變量(Variable)是TensorFlow.js中數(shù)據(jù)的主要表現(xiàn)形式灯萍,兩者不同之處在于張量是不可變的,而變量是可變的寸谜。
(一)張量(Tensors)
張量=容器竟稳,張量是現(xiàn)代機(jī)器學(xué)習(xí)的基礎(chǔ)。它的核心是一個(gè)數(shù)據(jù)容器熊痴,多數(shù)情況下他爸,它包含數(shù)字,有時(shí)候它也包含字符串果善,但這種情況比較少诊笤。因此把它想象成一個(gè)數(shù)字的水桶。
張量是由一組數(shù)值形成一個(gè)或多個(gè)維度的數(shù)組巾陕。 張量實(shí)例具有定義數(shù)組形狀的形狀屬性讨跟。
Tensorflow.js中數(shù)據(jù)的主要表現(xiàn)形式就是tensor(張量):由 一組數(shù)值形成一維或多維數(shù)組。一個(gè)Tensor實(shí)例有一個(gè)shape屬性來(lái)定義這一組數(shù)值如何組成張量,而最主要的Tensor實(shí)例的構(gòu)造函數(shù)就是 tf.tensor 函數(shù)鄙煤,如下所示:
// 2x3 Tensor
const shape = [2, 3]; // 2 行, 3 列
const a = tf.tensor([1.0, 2.0, 3.0, 10.0, 20.0, 30.0], shape);
a.print(); // 打印張量值
// 輸出: [[1 , 2 , 3 ],
// [10, 20, 30]]
// shape也可以用下面的方式實(shí)現(xiàn):
const b = tf.tensor([[1.0, 2.0, 3.0], [10.0, 20.0, 30.0]]);
b.print();
// 輸出: [[1 , 2 , 3 ],
// [10, 20, 30]]
但是晾匠,為了構(gòu)造低秩張量,我們推薦使用下面的函數(shù)來(lái)增強(qiáng)代碼的可讀性:tf.scalar(零維), tf.tensor1d(一維), tf.tensor2d(二維), tf.tensor3d(三維)梯刚、tf.tensor4d(四維)以及 tf.ones(值全是1)或者tf.zeros(值全是0) 凉馆,如下所示:
const a = tf.scalar(3.14);
a.print(); // 輸出零維張量
const b = tf.tensor2d([[2, 3, 4], [5, 6, 7]]);
b.print(); // 輸出二維張量
const c = tf.zeros([2, 3]);
c.print(); // 輸出2行3列的值全是0的張量
const d = tf.ones([3, 5]);
d.print(); // 輸出3行5列的值全是1的張量
在TensorFlow.js中,張量是不變的; 一旦創(chuàng)建你就不能改變它們的值亡资。 但是澜共,您可以對(duì)它們執(zhí)行操作來(lái)生成新的張量。
(二)變量(Variable)
變量(Variables)是通過(guò)張量進(jìn)行初始化得到的锥腻。不像Tensor的值不可變嗦董,變量的值是可變的。你可以使用變量的assign方法分配一個(gè)新的tensor到這個(gè)變量上瘦黑,這是變量就會(huì)改變:
const initialValues = tf.zeros([5]);
const biases = tf.variable(initialValues); // 初始化biases
biases.print(); // 輸出: [0, 0, 0, 0, 0]
const updatedValues = tf.tensor1d([0, 1, 0, 1, 0]);
biases.assign(updatedValues); // 更新 biases的值
biases.print(); // 輸出: [0, 1, 0, 1, 0]
如上所示京革,首先使用tf.zeros得到一個(gè)張量,然后利用這個(gè)張量初始化得到一個(gè)變量幸斥,接著我們就可以打印這個(gè)變量存崖,并且通Object.prototype.toString.call(biases)方法可以判斷變量也是一個(gè)對(duì)象,接著睡毒,我們?cè)偕梢粋€(gè)張量来惧,然后變量調(diào)用assign方法傳入這個(gè)張量,就可以得到一個(gè)新的變量了演顾。
由此我們可以得出一個(gè)結(jié)論:變量由張量生成供搀,且張量不可變而變量可變。
二钠至、Tensorflow.js 模型
在Tensorflow.js中葛虐,從概念上來(lái)說(shuō),一個(gè)模型就是一個(gè)給定一些輸入將會(huì)產(chǎn)生特定的輸出的函數(shù)棉钧。簡(jiǎn)單來(lái)說(shuō)屿脐,一個(gè)模型就是一個(gè)函數(shù),只是它完成了特定的任務(wù)。
在TensorFlow.js中有兩種方式來(lái)創(chuàng)建模型的诵,一種是通過(guò)操作(ops)來(lái)直接完成模型本身所做的工作万栅,另外一種就是通過(guò)高級(jí)API tf.model來(lái)創(chuàng)建一個(gè)模型,顯然第二種是更容易的西疤。
我們先看第一種創(chuàng)建模型的方法:
function predict(input) {
// y = a * x ^ 2 + b * x + c
// More on tf.tidy in the next section
return tf.tidy(() => {
const x = tf.scalar(input);
const ax2 = a.mul(x.square());
const bx = b.mul(x);
const y = ax2.add(bx).add(c);
return y;
});
}
const a = tf.scalar(2);
const b = tf.scalar(4);
const c = tf.scalar(8);
const result = predict(2);
result.print();
如上所示烦粒,我們定義的predict函數(shù)就是一個(gè)模型,對(duì)于給定的輸入代赁,我們就可以得到預(yù)測(cè)的輸出扰她。注意:所有的數(shù)字都需要經(jīng)過(guò)tf.scalar()張量處理。
而第二種創(chuàng)建模型的方法就是用 TensorFlow.js 中的 tf.model 方法(這里的model并不是真正可以調(diào)用的方法芭碍,而是一個(gè)總稱徒役,比如實(shí)際上可以調(diào)用的是tf.sequential模型),這在深度學(xué)習(xí)中是非常流行的概念窖壕。 下面的代碼就創(chuàng)建了 tf.sequential 模型:
const model = tf.sequential();
model.add(
tf.layers.simpleRNN({
units: 20,
recurrentInitializer: 'GlorotNormal',
inputShape: [80, 4]
})
);
const optimizer = tf.train.sgd(LEARNING_RATE);
model.compile({optimizer, loss: 'categoricalCrossentropy'});
model.fit({x: data, y: labels)});
三廉涕、Tensorflow.js 內(nèi)存管理
因?yàn)門ensorFlow.js使用了GPU來(lái)加速數(shù)學(xué)運(yùn)算,因此當(dāng)tensorflow處理張量和變量時(shí)就有必要來(lái)管理GPU內(nèi)存艇拍。在TensorFlow.js中狐蜕,我們可以通過(guò)dispose 和 tf.tidy這兩種方法來(lái)管理內(nèi)存。
(一)dispose
您可以在張量或變量上調(diào)用dispose來(lái)清除它并釋放其GPU內(nèi)存:
const x = tf.tensor2d([[0.0, 2.0], [4.0, 6.0]]);
const x_squared = x.square();
x.dispose();
x_squared.dispose();
(二)tf.tidy
進(jìn)行大量的張量操作時(shí)使用dispose可能會(huì)很麻煩卸夕。 TensorFlow.js提供了另一個(gè)函數(shù)tf.tidy层释,它對(duì)JavaScript中的常規(guī)范圍起到類似的作用,不同的是它針對(duì)GPU支持的張量快集。
tf.tidy執(zhí)行一個(gè)函數(shù)并清除所有創(chuàng)建的中間張量贡羔,釋放它們的GPU內(nèi)存。 它不清除內(nèi)部函數(shù)的返回值个初。
const average = tf.tidy(() => {
const y = tf.tensor1d([1.0, 2.0, 3.0, 4.0]);
const z = tf.ones([4]);
return y.sub(z).square().mean();
});
average.print();
使用tf.tidy將有助于防止應(yīng)用程序中的內(nèi)存泄漏乖寒。它也可以用來(lái)更謹(jǐn)慎地控制內(nèi)存何時(shí)回收。
兩個(gè)重要的注意事項(xiàng):
1院溺、傳遞給tf.tidy的函數(shù)應(yīng)該是同步的楣嘁,并且不會(huì)返回Promise。我們建議在tf.tidy內(nèi)不要有更新UI或在發(fā)出遠(yuǎn)程請(qǐng)求的代碼珍逸。
2逐虚、tf.tidy不會(huì)清理變量。變量通常持續(xù)到機(jī)器學(xué)習(xí)模型的整個(gè)生命周期谆膳,因此TensorFlow.js不會(huì)清理它們叭爱,即使它們是在tidy中創(chuàng)建的。不過(guò)漱病,您可以手動(dòng)調(diào)用dispose處理它們买雾。
四把曼、實(shí)例
(一)使用tensorflow進(jìn)行線性回歸
下面是一個(gè)使用tensorflow.js進(jìn)行線性回歸的案例,完整代碼如下:
<!DOCTYPE html>
<html style="height: 100%">
<head>
<meta charset="utf-8">
</head>
<body style="height: 100%; margin: 0">
<div id="container" style="height: 100%"></div><!-- 繪圖區(qū)域 -->
<script type="text/javascript" src="https://echarts.baidu.com/gallery/vendors/echarts/echarts.min.js"></script> <!-- 引入echart進(jìn)行繪圖 -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.14.2/dist/tf.min.js"></script><!-- 引入tersorflow.js -->
<script type="text/javascript">
function generateData(numPoints, coeff, sigma = 0.04) {//產(chǎn)生偽隨機(jī)數(shù)
return tf.tidy(() => {
const [k, b] = [tf.scalar(coeff.k),tf.scalar(coeff.b)];
const xs = tf.randomUniform([numPoints], -1, 1);//x坐標(biāo)
const ys = k.mul(xs).add(b)//y坐標(biāo)
.add(tf.randomNormal([numPoints], 0, sigma));//疊加噪聲
return {xs, ys: ys};
})
}
// Step 1. 要回歸的變量
const k = tf.variable(tf.scalar(Math.random()));
const b = tf.variable(tf.scalar(Math.random()));
// Step 2. 選取優(yōu)化器漓穿、迭代次數(shù)等參數(shù)
const numIterations = 75;
const learningRate = 0.5;
const optimizer = tf.train.sgd(learningRate);
// Step 3. 預(yù)測(cè)函數(shù)嗤军,定義為線性函數(shù)y = k * x + b
function predict(x) {// y = k * x + b
return tf.tidy(() => {return k.mul(x).add(b);});
}
// Step 4. 計(jì)算方差,方差越小說(shuō)明預(yù)測(cè)值越精確
function loss(prediction, labels) {
const error = prediction.sub(labels).square().mean();
return error;
}
// Step 5. 訓(xùn)練函數(shù)
async function train(xs, ys, numIterations) {
for (let iter = 0; iter < numIterations; iter++) {
//優(yōu)化并使方差最小
optimizer.minimize(() => {
const pred = predict(xs);//根據(jù)輸入數(shù)據(jù)預(yù)測(cè)輸出值
return loss(pred, ys);//計(jì)算預(yù)測(cè)值與訓(xùn)練數(shù)據(jù)間的方差
});
await tf.nextFrame();//
}
}
//機(jī)器學(xué)習(xí)
async function learnCoefficients() {//
const trueCoefficients = {k: 0.6, b: 0.8};//真實(shí)值
const trainingData = generateData(100, trueCoefficients);//用于模型訓(xùn)練的數(shù)據(jù)
await train(trainingData.xs, trainingData.ys, numIterations);// 模型訓(xùn)練
var xvals = await trainingData.xs.data();//訓(xùn)練數(shù)據(jù)的x坐標(biāo)值
var yvals = await trainingData.ys.data();//訓(xùn)練數(shù)據(jù)的y坐標(biāo)值
var sDatas = Array.from(yvals).map((y,i) => {return [xvals[i],yvals[i]]});//整理訓(xùn)練數(shù)據(jù)以便繪圖
console.log("k&b:",k.dataSync()[0],b.dataSync()[0]);//經(jīng)過(guò)訓(xùn)練后的系數(shù)
showResult(sDatas,k.dataSync()[0],b.dataSync()[0]);
}
//使用echart繪制結(jié)果
function showResult(scatterData,k,b){
var dom = document.getElementById("container");
var myChart = echarts.init(dom);
function realFun(x){return 0.6*x+0.8;}//理想曲線
function factFun(x){return k*x+b;}//回歸后的曲線
var realData = [[-1,realFun(-1)],[1,realFun(1)]];
var factData = [[-1,factFun(-1)],[1,factFun(1)]];
var option = {
title: {text: '線性回歸',left: 'left'},
tooltip: {trigger: 'axis',axisPointer: {type: 'cross'}},
xAxis: {type: 'value',splitLine: {lineStyle: {type: 'dashed'}},},
yAxis: {type: 'value',splitLine: {lineStyle: {type: 'dashed'}}},
series: [{
name: '離散點(diǎn)',type: 'scatter',
label: {
emphasis: {
show: true,
position: 'left',
textStyle: {
color: 'blue',
fontSize: 16
}
}
},
data: scatterData
},
{name: '理想曲線',type: 'line',showSymbol: false,data: realData,},
{name: '回歸曲線',type: 'line',showSymbol: false,data: factData,},],
legend: {data:['離散點(diǎn)','理想曲線','回歸曲線']},//圖例文字
};
myChart.setOption(option, true);
}
learnCoefficients();
</script>
</body>
</html>
個(gè)人主頁(yè):https://whl1207.github.io/