tensorflow.js示例筆記 - predict-download-time

預測下載時間。

<!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>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章