預測下載時間。
<!DOCTYPE html> <html> <head> <title>predict-download-time</title> <style> canvas { border: 1px solid #d3d3d3; } </style> <script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs"></script> </head> <body> </body> <script> window.onload = async function (){ const trainData = { sizeMB: [ 0.080, 9.000, 0.001, 0.100, 8.000, 5.000, 0.100, 6.000, 0.050, 0.500, 0.002, 2.000, 0.005, 10.00, 0.010, 7.000, 6.000, 5.000, 1.000, 1.000 ], timeSec: [ 0.135, 0.739, 0.067, 0.126, 0.646, 0.435, 0.069, 0.497, 0.068, 0.116, 0.070, 0.289, 0.076, 0.744, 0.083, 0.560, 0.480, 0.399, 0.153, 0.149 ] }; const testData = { sizeMB: [ 5.000, 0.200, 0.001, 9.000, 0.002, 0.020, 0.008, 4.000, 0.001, 1.000, 0.005, 0.080, 0.800, 0.200, 0.050, 7.000, 0.005, 0.002, 8.000, 0.008 ], timeSec: [ 0.425, 0.098, 0.052, 0.686, 0.066, 0.078, 0.070, 0.375, 0.058, 0.136, 0.052, 0.063, 0.183, 0.087, 0.066, 0.558, 0.066, 0.068, 0.610, 0.057 ] }; // 創建一個二維張量。第一個參數爲張量的值,第二個參數爲張量的形狀即shape。 // 但這裏的二維張量並不表示二維空間,而是指二秩張量或二軸張量。通常幾維就等 // 於幾軸、幾秩序,有關術語如下: // // 形狀:張量的每個軸的長度(元素數量)。 // 秩:張量的軸數。標量的秩爲0,向量的秩爲1,矩陣的秩爲2。 // 軸或維度:張量的一個特殊維度。 // 大小:張量的總項數,即形狀矢量元素的乘積。 // // 0維張量:標量(非數組) // 1維張量:矢量(一元數組) // 2維張量:矩陣(二元數組) // 3維張量:矩陣數組(rgb三通道就可以表示爲這個) // 4維張量:矩陣數組的數組(從這裏開始相當於從0維張量重新計算,但每個元素已經是是3維張量了) // 5維張量:矩陣數組的數組的數組 // ... // N維張量 const trainTensors = { sizeMB: tf.tensor2d(trainData.sizeMB, [20, 1]), // 輸入數據,單特徵。 timeSec: tf.tensor2d(trainData.timeSec, [20, 1]) // 標籤。 }; // 創建測試用張量。 const testTensors = { sizeMB: tf.tensor2d(testData.sizeMB, [20, 1]), timeSec: tf.tensor2d(testData.timeSec, [20, 1]) }; // 除了通過上述傳2個參數的方式創建張量,還可以直接傳多維數組來創建張量,即只需要傳一個參數 // 就行,比如: // tf.tensor2d([[1, 2], [3, 4]]); // 等同於: // tf.tensor2d([1, 2, 3, 4], [2, 2]); // 其中[2, 2]表示張量的形狀,即有2個軸(數組元素個數),每個軸的元素數量爲2。 // ***軸一般按照從全局到局部的順序進行排序:首先是批次軸,隨後是空間維度週週,最後是每個位 // 置的特徵。這樣,在內存中,特徵向量就會位於連續的區域。 // 創建一個線性迴歸模型。 const model = tf.sequential(); // 給模型添加一個稠密層,我們在這裏的輸入期望爲只有一個值的一維張量,操作方式也支持在上面的 // 創建模型的代碼處以傳參的方式添加。 // 對於線性迴歸模型的單層稠密層模型來說,kernel(內核、斜率)和bias(偏差)爲可調的兩個參數, // 下面內容會詳細介紹。 // keras中的dense稠密層又稱爲full connected全連接的層。它表示每一個結點都與上一層的所 // 有結點相連,用來把前邊提取到的特徵綜合起來。 // 層有關的配置都是針對keras的配置,要深入研究的話,可以查看keras和神經網絡的教程。 // ***對於tfjs來說,它並沒有像tf一樣直接引用keras,而是實現了類似於keras的API,因此我們 // 也可以將其看爲是引入了Keras,對於一些高級API的概念性理解上,可以這麼認爲。 model.add(tf.layers.dense({ inputShape: [1], // units代表該層的輸出維度或神經元個數, units解釋爲神經元個數爲了方便計算參數量,解釋 // 爲輸出維度爲了方便計算維度。 units: 1 })); // 配置模型訓練選項。 model.compile({ // 優化器用於網絡根據數據和損失函數更新其係數的算法。這裏指定爲隨機梯度下降算法。 // ***梯度下降是深度學習背後的最基本的算法結構,其實現爲反向傳播。 optimizer: 'sgd', // 損失函數用於誤差測量(平均絕對誤差)。這是如何在訓練數據上測量網絡的性能。損失越少越好, // 當我們訓練時,我們應該能夠計算出隨着時間的推移所造成的損失,並看到誤差的下降。如果我們 // 的模型訓練了很長一段時間,損失沒有減少,這可能意味着我們的模型沒有習得去擬合數據。 loss: 'meanAbsoluteError' }); // 對數據集以固定迭代數量的週期來訓練模型,這一過程中,模型會對數據進行擬合。 await model.fit( trainTensors.sizeMB, // 輸入。 trainTensors.timeSec, // 輸出。 {epochs: 10} // 訓練的迭代週期爲10次。(先定爲該值,後面會跟隨註釋的說明來更改) // {epochs: 200} // 訓練的迭代週期爲200次。 ); // 評估模型。evaluate與fit類似,區別在於評估模型不會去更新模型的係數,不會改變模型, // 評估就是計算平均損耗(誤差),提供特徵(輸入)和目標數據(輸出)來計算損耗,用於瞭解該模型在預測上的表現。 // meanAbsoluteError損耗函數,用於計算預測與目標之間的距離,取其絕對值(使它們全部爲正), // 然後返回這些值的平均值。 model.evaluate(testTensors.sizeMB, testTensors.timeSec).print(); // epochs爲10的時候,誤差在0.7-2.8之間。 // 由於默認情況下模型是從隨機初始狀態訓練而來的,梯度下降也將有隨機性,因此每次執行將獲得不同的值。 // 手動計算測試數據集的誤差(平均絕對誤差),得到與實際值相差多少的平均值,值約爲0.22。 const avgDelaySec = tf.mean(trainData.timeSec); // 0.295。 tf.mean(tf.abs(tf.sub(testData.timeSec, avgDelaySec))).print(); // 0.2202250212430954。 // 從這個評估的輸出我們可以發現,評估模型的輸出結果很糟糕。而與評估值下面的我們手動計算的 // 誤差來做對比,發現我們手動計算的誤差其實更低。0.7-2.8之間,可是遠大於0.22了。換句話說, // 這就意味着我們模型的準確性比我們手動計算的準確性還差。說明模型尚未學習到數據的真實結構, // 模型和數據並未充分擬合(欠擬合)。 // 稠密層模型在訓練時,就是一個簡單的線性方程:output = kernel * input + bias, // 在訓練過程中output和input由我們提供的數據集來決定,相對來說是固定的。而kernel和bias // 參數則是在一開始被賦值爲較小的隨機值,我們稱之爲隨機初始化。在訓練過程中參數kernel和bias // 偏差值會逐步更新,也就是通過梯度下降的方式來進行優化。但太少的訓練週期(epochs: 10)很 // 難使參數接近最佳值,因此我們繼續加大訓練迭代週期,解決欠擬合問題,然後再次評估修改迭代周 // 期後的模型。因此對調用fit方法的傳入參數做以下修改,這也稱之爲調整超參數: // await model.fit( // ... // {epochs: 200} // 訓練的迭代週期爲200次。 // ); // 然後我們再通過評估模型得到的平均誤差絕大多數時候都在0.055以內,這比我們手動計算的平均誤差0.295 // 還要精確5倍以上。 // 從實際的梯度下降曲線(模型損耗隨訓練迭代週期變化)來看,當迭代週期從50起時,已基本處於良好擬合的位置。 // ***注意:模型的數據擬合程度不是越大越好,超過了合理的範圍,可能會造成過度擬合(過擬合), // 相當於模型已經開始通過死記硬背的方式去學習訓練數據,這樣也會造成較大的誤差。如何避免 // 過擬合和更隱蔽的過擬合是深度學習中一直需要面對的問題。 // 然後我們準備一組預測數據,來檢驗下我們的模型的真實效果。 // ***注意:這裏需要將epochs重設爲200. const smallFileMB = 1; const bigFileMB = 100; const hugeFileMB = 10000; const predictSizeMBTensor = tf.tensor2d([[smallFileMB], [bigFileMB], [hugeFileMB]]); model.predict(predictSizeMBTensor).print(); // 我們得到的預測結果如下(多次predict的結果也不是不同的,這裏取平均的一個): // [[0.1342551], [6.4955053], [642.6205444]] // 1MB = > 0.1342551 // 100MB => 6.4955053 // 10000MB => 642.6205444 // 對於10000MB來講,我們的訓練數據中,沒有任何接近此打下的示例,因此對該數據的預測是非常冒險的。 // 好了,這裏總結一下,僅通過以下短短几行代碼,我們就能進行訓練、評估模型,也能通過模型做預測。 // 庫和高級API的存在極大低降低了入門深度學習的難度。 // const model = tf.sequential([tf.layers.dense({inputShape: [1], units: 1})]); // model.compile({optimizer: 'sgd', loss: 'meanAbsoluteError'}); // (async () => await model.fit(trainTensors.sizeMB, trainTensors.timeSec, {epochs: 10}))(); // model.evaluate(testTensors.sizeMB, testTensors.timeSec); // model.predict(tf.tensor2d([[7.8]])).print(); }; </script> </html>