大話性能測試系列(1)- 性能測試的基本概念

如果你對性能測試感興趣,但是又不熟悉理論知識,可以看下面的系列文章

https://www.cnblogs.com/poloyy/category/1620792.html

 

學習前的認知

我們在學習性能測試之前,需要有個新的認識:性能測試,不再是像功能測試一樣單純的找 Bug,而是去找性能指標

 

轉變思維

  • 在做功能測試、自動化測試的時候,我們基本都是依託界面進行測試,也稱 GUI 測試,我們的目的就是為了跑通功能、程序,並成功找到 Bug
  • 但在做性能測試的時候,我們大部分是 headless 模式(所謂的:無頭,無界面模式),目的不再是單純的為了找到 Bug,而是要分析性能指標等等(後續講到)

 

性能測試的時間一般會比自動化、功能測試長,為啥?

  • 因為性能測試的步驟跟自動化、功能測試的步驟不一樣,比如說前期的準備(了解系統,環境搭建),後期的壓力測試(7*24h)等等
  • 在後面,我們通過講述性能測試步驟來仔細了解

 

性能測試一定要工具,手工不行嗎?

  • 性能測試是模擬系統在被很多很多用戶同時使用時,系統能不能正常使用和提供服務
  • 重點:很多很多用戶
  • 功能測試:一個人點點點就知道功能通不通,有沒有 Bug 了
  • 性能測試:用手工的話,可以模擬幾個、十幾個用戶,但是當需要模擬上千萬個用戶時,手工又怎麼模擬數據量多的場景呢?
  • 類比,吃飯場景:一個人可以吃好幾碗,但是叫你吃幾百碗是不可能的
  • 結論:工具就可以模擬大數據量的場景,可以做到人做不到的事情

 

大數據量測試是性能測試嗎?

大數據量測試

簡單理解:一個接口返回的數據比較多(假設:不使用分頁,把所有數據同時返回)

 

結論

  • 返回大數據量的接口的響應時間會變長
  • 這麼大的數據量,我們需要考慮:網絡傳輸數據、服務器查詢這些數據、服務器處理這些數據等等分別需要多少時間
  • 這已經跟響應時間掛鈎,所以已經屬於性能測試的範圍,但不歸納於性能分析範圍

 

大數據測試是性能測試嗎?

大數據測試的功能屬於功能測試哦

 

性能測試過程發現問題需要立即提交嗎?

在性能測試過程中發現一些問題,假設定位到某一段代碼有問題,可以截圖提交 Bug 給開發,但這並不是我們性能測試的最終目的,最終目的是找出性能指標

 

有哪些性能指標?

  • 比如說響應時間:10個人、100個人 、1000個人 、10000個人向服務器發起請求,服務器響應請求的平均響應時間是多少,這就是一個指標
  • 又好比TPS:服務器在當前的配置下,不同用戶數發起請求,服務器的 TPS 處理能力是多少,這也是一個指標
  • 後續詳細介紹

 

性能測試中發現的 Bug 

  • 性能測試過程中發現的 Bug 屬於一個衍生品,並不是最終得到的結果
  • 但像功能測試,最終目的就是為了找出 Bug

 

關於這個問題的總結

  • 做性能測試,當數據量變大后,會出現連接超時、連接拒絕、500、502異常問題;在性能測試中,這些異常問題基本都會出現的,但不會去立即提 Bug
  • 對於性能測試工程師,我們要做的是分析為什麼在當前數據量下會出現連接超時、連接拒絕,響應時間超時、服務器異常等異常問題
  • 這就需要我們去分析性能瓶頸,並不會單獨去某個異常問題出現在哪裡,而是分析為什麼會出現這個異常問題,分析的是服務器或者是代碼,而不是讓開發人員馬上來修復這些異常問題

 

我們常說的壓測是指壓力測試嗎?

  • 並不是,而是指負載測試,一般都是為了找出系統的最大負載量
  • 就好像你老闆說:你去壓測下,看看系統能支撐多少用戶同時訪問我們的系統

 

什麼是性能測試?

狹義理解

  • 通過工具,找出或獲得系統在不同工況下的性能指標值
  • 性能測試過程中,重點是找出性能指標,而不再是找出 Bug,
  • 性能測試的產出絕對不只是 Bug

 

場景類比

跑步100米,用時多少?運動員的心跳、步伐頻率是多少?

  1. 跑步100米:業務場景
  2. 用時多少:響應時間
  3. 運動員的心跳、步伐:性能指標值

性能指標值和響應時間是否滿足當前業務場景的最低要求(合格線)

 

什麼時候能找出性能指標值

假設當前有一個業務

電商系統,下單業務,目前還不知道系統支持多少人同時下單,那麼我們需要找到服務器能正常支持多少人同時下單

 

性能測試初始階段(第一次做)

  • 先把基礎的性能指標值找出來(第一次性能測試也叫做基準測試)
  • 比如:100個人同時下單系統正常,但120個人同時下單就會出現部分請求的響應時間超長,連接異常
  • 那麼100-120範圍內的某個值就是當前服務器能達到的性能指標值(基準值)

 

版本迭代,進行第二次做性能測試,重新跑一遍之前的性能腳本

  • 又會得到一些性能指標值,對比上個版本的性能指標值,看是否有優化(性能變化)
  • 假設這個時候120個人同時下單是正常的,150個人才有異常,那麼接口已經有優化了

 

假設公司是從0開始做性能測試

  • 第一階段:做好性能測試,得到性能指標值
  • 第二階段:假設性能比之前差,哪些性能指標值不滿足預期值,就需要分析是哪裡有問題

 

廣義理解

  • 只要與服務器性能指標相關的測試都屬於性能測試
  • 比如:響應時間、併發用戶數、服務器處理能力、吞吐量等性能指標
  • 負載測試、壓力測試、容量測試、可靠性測試都屬於性能測試
  • 通常嘴巴上說的做性能測試就是廣義的性能測試,它包括了很多內容,並不只是針對某一個測試類型

 

“官方”解釋

以下含義來源高老的解釋,比較“官方”的術語

  1. 性能測試針對系統的性能指標,建立性能測試模型
  2. 制定性能測試方案
  3. 制定監控策略
  4. 在場景條件下執行性能場景
  5. 分析判斷性能瓶頸並調優
  6. 最終得出性能結果來評估系統的性能指標是否滿足既定值

其實也算是一個簡潔描述的性測試流程了

 

注意

  • 性能測試不像自動化測試那樣很多東西大家都是公認的,性能測試沒有一套標準的知識體系,只能說是相似的
  • 基本每個人都有自己的一套知識體系,就好像高老也會說他給性能測試的定義很大可能會被轟炸一樣
  • 只要屬於自己的知識體系建立起來了,那麼就能助力你正確的完成性能測試
  • 不用太過糾結於哪個人對性能測試概念的解釋是最準確的

目前博主是正在學習性能測試的小白一枚,希望通過通俗簡單的術語來學懂性能測試,打造屬於自己的知識體系,歡迎大家進群與我溝通(870155189)

 

 什麼是負載測試?

概念

  • 逐步增加系統負載,測試系統性能變化,並最終確定系統所能承受的最大負載量
  • 通俗理解:看看你幾斤幾兩

 

如何增加負載

通過增加“用戶數”,就是常說的併發數

 

場景類比

天平秤,稱東西的時候,需要逐步加砝碼,最終達到砝碼和物品重量的平衡點,因為它不可能一下子就達到平衡點【好比不可能一下子找到系統能承受的最大負載量】

  • 稱東西:業務場景
  • 加砝碼:逐步加壓
  • 達到平衡點:找到最大負載量

 

實際場景

  • 有一個業務,增加到40個人的時候,服務器還能正常使用,沒有異常
  • 當你增加到50個人的時候,服務器已經開始有異常了,那麼就能確定40-50之間某個值就是系統所能承受的最大負載量【出現性能拐點,找到了服務器性能瓶頸的範圍值】
  • 最後減小加壓梯度(比如:從40個人開始每次增加1個人、2個人),確認最大負載量【確認性能拐點】

 

服務器又有哪些可能會出現的異常呢

  • 響應時間超長:正常服務器處理請求時間是 1s,但現在變成3s – 5s
  • 服務報錯:無法同時正常響應多個請求
  • 服務器宕機:系統完全用不了

 

什麼是壓力測試?

概念

  • 在較大的性能壓力下,持續運行一個比較長的時間,看看系統服務是否正常及系統資源的利用率情況
  • 通俗理解:鴨梨山大!
  • 關鍵字:較大壓力 + 較長時間
  • 注意:不是滿負荷壓力哦

 

場景類比

問:大家什麼時候會覺得工作壓力大?

答:996、007;因為你不會覺得955壓力山大吧

結論:所以在我們日常工作中,長時間工作強度高,才會覺得壓力大;如果你一周就加班一天也說壓力大…(那就別干這一行了)

 

壓力測試用來幹嘛的

測試系統的穩定性

 

類比

工作壓力大,你還能堅持下去(那穩定性肯定好吧),那如果你很快就離職了(那穩定性肯定差,都宕機罷工了)

 

什麼時候會做壓力測試

  • 生產環境下,系統隔三差五的出現不穩定的情況
  • 這個時候,就需要通過壓力測試去測試系統的穩定性情況

 

啥情況算不穩定?穩定性差?

隔三差五的出現下面的情況

  • 服務異常:響應錯誤、響應時間超時等
  • 服務器出現異常:宕機

 

怎麼分析是服務異常還是服務器異常 

  • 如果所有請求都是一片紅,應用程序發送的所有請求都報紅,就是服務器出現了異常
  • 如果有些請求偶爾成功響應,偶爾又失敗,則是服務異常,出現不穩定的情況

 

如何取壓力值

  • 在負載測試中,我們確認了系統所能承受的最大負載量
  • 壓力值 < 最大負載量,一般取80%左右

 

靈魂拷問

負載測試一般時間比較短,壓力測試時間比較長,持續運行時間短就能正常使用,但持續運行時間長就可能崩掉了,這是什麼原因呢?

 

場景類比

  • 栗子一:電腦保持開機狀態很長時間,會逐漸變卡,因為內存的東西會越來越多,得不到有效的回收, 就會越來越卡
  • 栗子二:當你經常工作壓力很大,且你的心理所能承受的壓力逐漸達到最大值時,你就可能會選擇離職

 

總結

壓力測試長時間運行,可能會逐漸增加系統的內存佔用空間,若得不到有效的內存回收,當達到內存最大值時,系統就會崩掉

 

壓力測試持續運行時間要多久?

  • 標準性能測試裏面,一般是7*24小時,或者是它的倍數
  • 但是實際工作中,並不會這麼久,否則成本太高
  • 所以會以小時為單位,比如:4個小時、8個小時…晚上下班之後做,第二天早上上班看結果

 

先負載測試還是壓力測試?

  • 先負載測試
  • 負載測試可以找到服務器性能瓶頸的範圍值,若生產環境中系統穩定性較差,再做壓力測試
  • 所以壓力測試是可做可不做的

 

什麼是可靠性測試?

概念

  • 在給定的一定的業務壓力下,持續運行一段時間,查看系統是否穩定
  • 關鍵字:是否穩定,一定業務壓力
  • 注意:不是較大壓力哦

 

業務場景栗子

電商秒殺場景,幾十個商品幾十萬個人同時秒殺搶購

 

如何理解可靠性測試

  1. 編寫性能腳本:假設一秒內有一萬個人同時發起請求
  2. 有壓力嗎?,一萬個人同時發起請求
  3. 但是持續時間,不像壓力測試一樣需要持續一段時間
  4. 目的是為了驗證當這麼多人同時發起請求時,成功秒殺的用戶能否繼續完成後續下單付款等操作【一定業務壓力下,系統是否穩定運行】

 

什麼是容量測試?

概念

  • 在一定的軟、硬件條件下,在數據庫不同數據量級數據量的情況下,對系統中讀/寫比較多的業務進行測試,從而獲得不同數據量級下的性能指標值
  • 關鍵字:不同數據量級

 

數據庫數據量對性能測試結果有沒有影響?

肯定有

  • 比如數據庫已經有幾百條數據和幾百萬條數據,查詢的速度肯定不一樣,所以肯定會影響性能測試結果
  • 數據量級的差異,會影響TPS、響應時間、網絡等

 

場景類比

從一袋米中找一個綠豆,和一碗米中找一個綠豆,找的時間肯定是千差萬別的

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

Flink 如何分流數據

  • 場景
  • 分流方式
  • 如何分流
    • 使用Filter分流
    • 使用Split分流
    • 使用Side Output分流

場景

獲取流數據的時候,通常需要根據所需把流拆分出其他多個流,根據不同的流再去作相應的處理。

舉個例子:創建一個商品實時流,商品有季節標籤,需要對不同標籤的商品做統計處理,這個時候就需要把商品數據流根據季節標籤分流。

分流方式

  • 使用Filter分流
  • 使用Split分流
  • 使用Side Output分流

如何分流

先模擬一個實時的數據流

import lombok.Data;
@Data
public class Product {
    public Integer id;
    public String seasonType;
}

自定義Source

import common.Product;
import org.apache.flink.streaming.api.functions.source.SourceFunction;

import java.util.ArrayList;
import java.util.Random;

public class ProductStremingSource implements SourceFunction<Product> {
    private boolean isRunning = true;

    @Override
    public void run(SourceContext<Product> ctx) throws Exception {
        while (isRunning){
            // 每一秒鐘產生一條數據
            Product product = generateProduct();
            ctx.collect(product);
            Thread.sleep(1000);
        }
    }

    private Product generateProduct(){
        int i = new Random().nextInt(100);
        ArrayList<String> list = new ArrayList();
        list.add("spring");
        list.add("summer");
        list.add("autumn");
        list.add("winter");
        Product product = new Product();
        product.setSeasonType(list.get(new Random().nextInt(4)));
        product.setId(i);
        return product;
    }
    @Override
    public void cancel() {

    }
}

輸出:

使用Filter分流

使用 filter 算子根據數據的字段進行過濾。

import common.Product;
import org.apache.flink.streaming.api.datastream.DataStreamSource;
import org.apache.flink.streaming.api.datastream.SingleOutputStreamOperator;
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment;
import source.ProductStremingSource;

public class OutputStremingDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<Product> source = env.addSource(new ProductStremingSource());

        // 使用Filter分流
        SingleOutputStreamOperator<Product> spring = source.filter(product -> "spring".equals(product.getSeasonType()));
        SingleOutputStreamOperator<Product> summer = source.filter(product -> "summer".equals(product.getSeasonType()));
        SingleOutputStreamOperator<Product> autumn  = source.filter(product -> "autumn".equals(product.getSeasonType()));
        SingleOutputStreamOperator<Product> winter  = source.filter(product -> "winter".equals(product.getSeasonType()));
        source.print();
        winter.printToErr();

        env.execute("output");
    }
}

結果輸出(紅色為季節標籤是winter的分流輸出):

使用Split分流

重寫OutputSelector內部類的select()方法,根據數據所需要分流的類型反正不同的標籤下,返回SplitStream,通過SplitStream的select()方法去選擇相應的數據流。

只分流一次是沒有問題的,但是不能使用它來做連續的分流。

SplitStream已經標記過時了

public class OutputStremingDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<Product> source = env.addSource(new ProductStremingSource());

        // 使用Split分流
        SplitStream<Product> dataSelect = source.split(new OutputSelector<Product>() {
            @Override
            public Iterable<String> select(Product product) {
                List<String> seasonTypes = new ArrayList<>();
                String seasonType = product.getSeasonType();
                switch (seasonType){
                    case "spring":
                        seasonTypes.add(seasonType);
                        break;
                    case "summer":
                        seasonTypes.add(seasonType);
                        break;
                    case "autumn":
                        seasonTypes.add(seasonType);
                        break;
                    case "winter":
                        seasonTypes.add(seasonType);
                        break;
                    default:
                        break;
                }
                return seasonTypes;
            }
        });
        DataStream<Product> spring = dataSelect.select("machine");
        DataStream<Product> summer = dataSelect.select("docker");
        DataStream<Product> autumn = dataSelect.select("application");
        DataStream<Product> winter = dataSelect.select("middleware");
        source.print();
        winter.printToErr();

        env.execute("output");
    }
}

使用Side Output分流

推薦使用這種方式

首先需要定義一個OutputTag用於標識不同流

可以使用下面的幾種函數處理流發送到分流中:

  • ProcessFunction
  • KeyedProcessFunction
  • CoProcessFunction
  • KeyedCoProcessFunction
  • ProcessWindowFunction
  • ProcessAllWindowFunction

之後再用getSideOutput(OutputTag)選擇流。

public class OutputStremingDemo {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        DataStreamSource<Product> source = env.addSource(new ProductStremingSource());

        // 使用Side Output分流
        final OutputTag<Product> spring = new OutputTag<Product>("spring");
        final OutputTag<Product> summer = new OutputTag<Product>("summer");
        final OutputTag<Product> autumn = new OutputTag<Product>("autumn");
        final OutputTag<Product> winter = new OutputTag<Product>("winter");
        SingleOutputStreamOperator<Product> sideOutputData = source.process(new ProcessFunction<Product, Product>() {
            @Override
            public void processElement(Product product, Context ctx, Collector<Product> out) throws Exception {
                String seasonType = product.getSeasonType();
                switch (seasonType){
                    case "spring":
                        ctx.output(spring,product);
                        break;
                    case "summer":
                        ctx.output(summer,product);
                        break;
                    case "autumn":
                        ctx.output(autumn,product);
                        break;
                    case "winter":
                        ctx.output(winter,product);
                        break;
                    default:
                        out.collect(product);
                }
            }
        });

        DataStream<Product> springStream = sideOutputData.getSideOutput(spring);
        DataStream<Product> summerStream = sideOutputData.getSideOutput(summer);
        DataStream<Product> autumnStream = sideOutputData.getSideOutput(autumn);
        DataStream<Product> winterStream = sideOutputData.getSideOutput(winter);

        // 輸出標籤為:winter 的數據流
        winterStream.print();

        env.execute("output");
    }
}

結果輸出:

更多文章:www.ipooli.com

掃碼關注公眾號《ipoo》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

時間序列神器之爭:Prophet VS LSTM

一、需求背景

我們福祿網絡致力於為廣大用戶提供智能化充值服務,包括各類通信充值卡(比如移動、聯通、電信的話費及流量充值)、遊戲類充值卡(比如王者榮耀、吃雞類點券、AppleStore充值、Q幣、鬥魚幣等)、生活服務類(比如肯德基、小鹿茶等),網娛類(比如QQ各類鑽等),作為一個服務提供商,商品質量的穩定、持續及充值過程的便捷一直是我們在業內的口碑。
在整個商品流通過程中,如何做好庫存的管理,以充分提高庫存運轉周期和資金使用效率,一直是個難題。基於此,我們提出了智能化的庫存管理服務,根據訂單數據及商品數據,來預測不同商品隨着時間推移的日常消耗情況。

二、算法選擇

目前成熟的時間序列預測算法很多,但商業領域性能優越的卻不多,經過多種嘗試,給大家推薦2種時間序列算法:facebook開源的Prophet算法和LSTM深度學習算法。
現將個人理解的2種算法特性予以簡要說明:

  • (1)、在訓練時間上,prophet幾十秒就能出結果,而lstm往往需要1個半小時,更是隨着網絡層數和特徵數量的增加而增加。
  • (2)、Prophet是一個為商業預測而生的時間序列預測模型,因此在很多方便都有針對性的優化,而lstm的初衷是nlp。
  • (3)、Prophet無需特徵處理即可使用,參數調優也明確簡單。而lstm則需要先進行必要的特徵處理,其次要進行正確的網絡結構設計,因此lstm相對prophet更為複雜。
  • (4)、Lstm需要更多的數據進行學習,否則無法消除欠擬合的情形。而prophet不同,prophet基於統計學,有完整的數學理論支撐,因此更容易從少量的數據中完成學習。
  • (5)、傳統的時間序列預測算法只支持單緯度,但LSTM能支持多緯度,也就是說LSTM能考慮促銷活動,目標用戶特性,產品特性等

三、數據來源

  • (1)、訂單數據
  • (2)、產品分類數據

四、數據形式

time,product,cnt
2019-10-01 00,**充值,6
2019-10-01 00,***遊戲,368
2019-10-01 00,***,1
2019-10-01 00,***,11
2019-10-01 00,***遊戲,17
2019-10-01 00
,三網***,39
2019-10-01 00,**網,6
2019-10-01 00,***,2

字段說明:

  • Time:小時級時間
  • Product:產品名稱或產品的分類名稱,目前使用的是產品2級分類,名稱
  • Cnt:成功訂單數量
    目前的時間序列是由以上time和cnt組成,product是用於區分不同時間序列的字段。

五、特徵處理

時間序列一般不進行特徵處理,當然可以根據具體情況進行歸一化處理或是取對數處理等。

六、算法選擇

目前待選的算法主要有2種:

  • (1)、Prophet
    Facebook開源的時間序列預測算法,考慮了節假日因素。
  • (2)、LSTM
    優化后的RNN深度學習算法。

七、算法說明

7.1 prophet

7.1.1Prophet的核心是調參,步驟如下:
  • 1、首先我們去除數據中的異常點(outlier),直接賦值為none就可以,因為Prophet的設計中可以通過插值處理缺失值,但是對異常值比較敏感。
  • 2、選擇趨勢模型,默認使用分段線性的趨勢,但是如果認為模型的趨勢是按照log函數方式增長的,可設置growth=’logistic’從而使用分段log的增長方式
  • 3、 設置趨勢轉折點(changepoint),如果我們知道時間序列的趨勢會在某些位置發現轉變,可以進行人工設置,比如某一天有新產品上線會影響我們的走勢,我們可以將這個時刻設置為轉折點。如果自己不設置,算法會自己總結changepoint。
  • 4、 設置周期性,模型默認是帶有年和星期以及天的周期性,其他月、小時的周期性需要自己根據數據的特徵進行設置,或者設置將年和星期等周期關閉。
    設置節假日特徵,如果我們的數據存在節假日的突增或者突降,我們可以設置holiday參數來進行調節,可以設置不同的holiday,例如五一一種,國慶一種,影響大小不一樣,時間段也不一樣。
  • 5、 此時可以簡單的進行作圖觀察,然後可以根據經驗繼續調節上述模型參數,同時根據模型是否過擬合以及對什麼成分過擬合,我們可以對應調節seasonality_prior_scale、holidays_prior_scale、changepoint_prior_scale參數。

以上是理論上的調參步驟,但我們在實際情況下在建議使用grid_search(網格尋參)方式,直接簡單效果好。當機器性能不佳時網格調參配合理論調參方法可以加快調參速度。建議初學者使用手動調參方式以理解每個參數對模型效果的影響。

holiday.csv

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from fbprophet import Prophet

data = pd.read_csv('../data/data2.csv', parse_dates=['time'], index_col='time')


def get_product_data(name, rule=None):
    product = data[data['product'] == name][['cnt']]
    product.plot()
    
if rule is not None:
        product = product.resample(rule).sum()
    product.reset_index(inplace=True)
    product.columns = ['ds', 'y']
    return product


holidays = pd.read_csv('holiday.csv', parse_dates=['ds'])
holidays['lower_window'] = -1

holidays = holidays.append(pd.DataFrame({
    'holiday': '雙11',
    'ds': pd.to_datetime(['2019-11-11', '2020-11-11']),
    'lower_window': -1,
    'upper_window': 1,
})).append(pd.DataFrame({
    'holiday': '雙12',
    'ds': pd.to_datetime(['2019-12-12', '2020-12-12']),
    'lower_window': -1,
    'upper_window': 1,
})
)

def predict(name, rule='1d', freq='d', periods=1, show=False):
    ds = get_product_data(name, rule=rule)
    if ds.shape[0] < 7:
        return None
    m = Prophet(holidays=holidays)
    m.fit(ds)
    future = m.make_future_dataframe(freq=freq, periods=periods)  # 建立數據預測框架,數據粒度為天,預測步長為一年
    forecast = m.predict(future)
    if show:
        m.plot(forecast).show()  # 繪製預測效果圖
        m.plot_components(forecast).show()  # 繪製成分趨勢圖
    mse = forecast['yhat'].iloc[ds.shape[0]] - ds['y'].values
    mse = np.abs(mse) / (ds['y'].values + 1)
    return [name, mse.mean(), mse.max(), mse.min(), np.quantile(mse, 0.9), np.quantile(mse, 0.8), mse[-7:].mean(),
            ds['y'].iloc[-7:].mean()]
if __name__ == '__main__':
    products = set(data['product'])
    p = []
    for i in products:
        y = predict(i)
        if y is not None:
            p.append(y)
    df = pd.DataFrame(p, columns=['product', 'total_mean', 'total_max', 'total_min', '0.9', '0.8', '7_mean',
       '7_real_value_mean'])
    df.set_index('product', inplace=True)
    product_sum: pd.DataFrame = data.groupby('product').sum()
    df = df.join(product_sum)
    df.sort_values('cnt', ascending=False, inplace=True)
    df.to_csv('result.csv', index=False)

結果如下:由於行數較多這裏只展示前1行

根據結果,對比原生數據,可以得出如下結論:
就算法與產品的匹配性可分為3個類型:

  • (1)與算法較為匹配,算法的歷史誤差8分為數<=0.2的
  • (2)與算法不太匹配的,算法的歷史誤差8分為數>0.2的
  • (3)數據過少的,無法正常預測的。目前僅top10就能佔到整體訂單數的90%以上。
7.1.2 部分成果展示

A. 因素分解圖

上圖中主要分為3個部分,分別對應prophet 3大要素,趨勢、節假日或特殊日期、周期性(包括年周期、月周期、week周期、天周期以及用戶自定義的周期)
下面依照上面因素分解圖的順序依次對圖進行說明:

  • (1)、Trend:
    即趨勢因素圖。描述時間序列的趨勢。Prophet支持線性趨勢和logist趨勢。通過growth參數設置,當然模型能自己根據時間序列的走勢判斷growth類型。這也是prophet實現的比較智能的一點。
  • (2)、Holidays
    即節假日及特殊日期因素圖。描述了節假日及用戶自定義的特殊日期對時間序列的影響。正值為正影響,負值為負影響。從圖中可以看出這個商品對節假日比較敏感。節假日是根據holidays參數設置的。
  • (3)、weekly
    星期周期性因素圖。正常情況下,如果是小時級別數據將會有天周期圖。有1年以上完整數據並且時間序列有典型的年周期性會有年周期圖。如果你覺得這個有年周期,但模型並不這麼認為,你可以通過設置yearly_seasonality設置一個具體的數值。這個數值默認情況下為10(weekly_seasonality默認為3),這個值代表的是傅里恭弘=叶 恭弘級數的項數,越大模型越容易過擬合,過小則會導致欠擬合,一般配合seasonality_prior_scale使用。
    B.預測曲線與實際值對比

7.2 lstm

LSTM(長短記憶網絡)主要用於有先後順序的序列類型的數據的深度學習網絡。是RNN的優化版本。一般用於自然語言處理,也可用於時間序列的預測。

簡單來說就是,LSTM一共有三個門,輸入門,遺忘門,輸出門, i 、o、 f 分別為三個門的程度參數, g 與RNN中的概念一致。公式里可以看到LSTM的輸出有兩個,細胞狀態c 和隱狀態 h,c是經輸入、遺忘門的產物,也就是當前cell本身的內容,經過輸出門得到h,就是想輸出什麼內容給下一單元。

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
from torch import nn

from sklearn.preprocessing import MinMaxScaler

ts_data = pd.read_csv('../data/data2.csv', parse_dates=['time'], index_col='time')


def series_to_supervised(data, n_in=1, n_out=1, dropnan=True):
    n_vars = 1 if type(data) is list else data.shape[1]
    df = pd.DataFrame(data)
    cols, names = list(), list()
    # input sequence (t-n, ... t-1)
    for i in range(n_in, 0, -1):
        cols.append(df.shift(i))
        names += [('var%d(t-%d)' % (j + 1, i)) for j in range(n_vars)]
    # forecast sequence (t, t+1, ... t+n)
    for i in range(0, n_out):
        cols.append(df.shift(-i))
        if i == 0:
            names += [('var%d(t)' % (j + 1)) for j in range(n_vars)]
        else:
            names += [('var%d(t+%d)' % (j + 1, i)) for j in range(n_vars)]
    # put it all together
    agg = pd.concat(cols, axis=1)
    agg.columns = names
    # drop rows with NaN values
    if dropnan:
        agg.dropna(inplace=True)
    return agg


def transform_data(feature_cnt=2):
    yd = ts_data[ts_data['product'] == '移動話費'][['cnt']]
    scaler = MinMaxScaler(feature_range=(0, 1))
    yd_scaled = scaler.fit_transform(yd.values)
    yd_renamed = series_to_supervised(yd_scaled
, n_in=feature_cnt).values.astype('float32')

    n_row = yd_renamed.shape[0]

    n_train = int(n_row * 0.7)

    train_X, train_y = yd_renamed[:n_train, :-1], yd_renamed[:n_train, -1]
    test_X, test_y = yd_renamed[n_train:, :-1], yd_renamed[n_train:, -1]

    # 最後,我們需要將數據改變一下形狀,因為 RNN 讀入的數據維度是 (seq, batch, feature),所以要重新改變一下數據的維度,這裏只有一個序列,所以 batch 是 1,而輸入的 feature 就是我們希望依據的幾天,這裏我們定的是兩個天,所以 feature 就是 2.
    train_X = train_X.reshape((-1, 1, feature_cnt))
    test_X = test_X.reshape((-1, 1, feature_cnt))
    print(train_X.shape, train_y.shape, test_X.shape, test_y.shape)

    # 轉化成torch 的張量
    train_x = torch.from_numpy(train_X)
    train_y = torch.from_numpy(train_y)
    test_x = torch.from_numpy(test_X)
    test_y = torch.from_numpy(test_y)
    return scaler, train_x, train_y, test_x, test_y


scaler, train_x, train_y, test_x, test_y = transform_data(24)


# lstm 網絡
class lstm_reg(nn.Module):  # 括號中的是python的類繼承語法,父類是nn.Module類 不是參數的意思
    def __init__(self, input_size, hidden_size, output_size=1, num_layers=2):  # 構造函數
        # inpu_size 是輸入的樣本的特徵維度, hidden_size 是LSTM層的神經元個數,
        # output_size是輸出的特徵維度
        super(lstm_reg, self).__init__()  # super用於多層繼承使用,必須要有的操作

        self.rnn = nn.LSTM(input_size, hidden_size, num_layers)  # 兩層LSTM網絡,
        self.reg = nn.Linear(hidden_size, output_size)  # 把上一層總共hidden_size個的神經元的輸出向量作為輸入向量,然後回歸到output_size維度的輸出向量中

    
def forward(self, x):  # x是輸入的數據
        x, _ = self.rnn(x)  # 單個下劃線表示不在意的變量,這裡是LSTM網絡輸出的兩個隱藏層狀態
        s, b, h = x.shape
        x = x.view(s * b, h)
        x = self.reg(x)
        x = x.view(s, b, -1)  # 使用-1表示第三個維度自動根據原來的shape 和已經定了的s,b來確定
        return x


def train(feature_cnt, hidden_size, round, save_path='model.pkl'):
    # 我使用了GPU加速,如果不用的話需要把.cuda()給註釋掉
    net = lstm_reg(feature_cnt, hidden_size)
    criterion = nn.MSELoss()
    optimizer = torch.optim.Adam(net.parameters(), lr=1e-2)
    for e in range(round):
        # 新版本中可以不使用Variable了
        #     var_x = Variable(train_x).cuda()
        #     var_y = Variable(train_y).cuda()

        # 將tensor放在GPU上面進行運算
        var_x = train_x
        var_y = train_y

        out = net(var_x)
        loss = criterion(out, var_y)

        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        if (e + 1) % 100 == 0:
            print('Epoch: {}, Loss:{:.5f}'.format(e + 1, loss.item()))
    # 存儲訓練好的模型參數
    torch.save(net.state_dict(), save_path)
    return net


if __name__ == '__main__':
    net = train(24, 8, 5000)
    # criterion = nn.MSELoss()
    # optimizer = torch.optim.Adam(net.parameters(), lr=1e-2)
    pred_test = net(test_x)  # 測試集的預測結果

    pred_test = pred_test.view(-1).data.numpy()  # 先轉移到cpu上才能轉換為numpy

    # 乘以原來歸一化的刻度放縮回到原來的值域
    origin_test_Y = scaler.inverse_transform(test_y.reshape((-1,1)))
    origin_pred_test = scaler.inverse_transform(pred_test.reshape((-1,1)))

    # 畫圖
    plt.plot(origin_pred_test, 'r', label='prediction')
    plt.plot(origin_test_Y, 'b', label='real')
    plt.legend(loc='best')
    plt.show()

    # 計算MSE
    # loss = criterion(out, var_y)?
    true_data = origin_test_Y
    true_data = np.array(true_data)
    true_data = np.squeeze(true_data)  # 從二維變成一維
    
MSE = true_data - origin_pred_test
    MSE = MSE * MSE
    MSE_loss = sum(MSE) / len(MSE)
    print(MSE_loss)

八、兩種算法的比較

  • (1)在訓練時間上,prophet幾十秒就能出結果,而lstm往往需要1個半小時,更是隨着網絡層數和特徵數量的增加而增加。
  • (2)Prophet是一個為商業預測而生的時間序列預測模型,因此在很多方便都有針對性的優化,而lstm的初衷是nlp。
  • (3)Prophet無需特徵處理即可使用,參數調優也明確簡單。而lstm則需要先進行必要的特徵處理,其次要進行正確的網絡結構設計,因此lstm相對prophet更為複雜。
  • (4)Lstm需要更多的數據進行學習,否則無法消除欠擬合的情形。而prophet不同,prophet基於統計學,有完整的數學理論支撐,因此更容易從少量的數據中完成學習。
    參考文獻:
    【1】Prophet官方文檔:https://facebook.github.io/prophet/
    【2】Prophet論文:https://peerj.com/preprints/3190/
    【3】Prophet-github:https://github.com/facebook/prophet
    【4】LSTM http://colah.github.io/posts/2015-08-Understanding-LSTMs/
    【5】基於LSTM的關聯時間序列預測方法研究 尹康 《北京交通大學》 2019年 cnki地址:http://cdmd.cnki.com.cn/Article/CDMD-10004-1019209125.htm

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

印度最大鱷魚動物園受疫情衝擊 陷資金短缺危機

摘錄自2020年8月11日中央社報導

印度馬德拉斯鱷魚動物園主管表示,實施防疫封鎖令後,遊客大量減少,造成門票收入劇減,動物園恐在4個月內耗盡資金,無法餵養動物、支付薪資以及進行研究。

位於印度南部城市清奈(Chennai)南方約40公里的馬德拉斯鱷魚庫(Madras Crocodile Bank),年銷售約500萬張門票,通常約占動物園營收半數。但馬德拉斯鱷魚庫園長傑蘇達桑(Allwin Jesudasan)表示,暑假季節實施封鎖期間,遊客減少將近250萬人,估計損失18萬7000美元(約新台幣550萬元)。

傑蘇達桑告訴路透社,「我們目前的資金情況僅能再維持運作3或4個月。」馬德拉斯鱷魚庫在網站發布募捐聲明表示:「我們的資深員工已主動減薪10%至50%,並大幅刪減園內活動,僅剩不可或缺活動。」資金耗盡後員工及動物的未來命運,無法立即知悉

生物多樣性
國際新聞
印度
動物園
武漢肺炎
動物與大環境變遷

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

網頁設計最專業,超強功能平台可客製化

砍樹種電 不顧居民反對的日本御嶽山麓太陽能爭議

文:宋瑞文(加州能源特約撰述)

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!

野生動物鑑識科技進步 找出穿山甲的「同位素指紋」助打擊走私

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※產品缺大量曝光嗎?你需要的是一流包裝設計!

巴西總統口出狂語 稱亞馬遜雨林大火是謊言

摘錄自2020年8月12日中央社報導

儘管巴西政府數據顯示,亞馬遜雨林的火災件數呈現增加趨勢,但總統波索納洛(Jair Bolsonaro)卻宣稱大火肆虐亞馬遜雨林一事僅是「謊言」。

波索納洛昨天與數個南美洲國家的領袖進行視訊會議。極右派的波索納洛在會議中表示:「熱帶雨林不會著火。所以,亞馬遜雨林失火的故事是個謊言,我們應以真實數據因應。」

然而,巴西國家太空署(INPE)的衛星影像顯示,今年7月巴西亞馬遜森林的火災件數,較去年7月增加28%,達到6803起。

法新社報導,專家認為亞馬遜森林的火災並非自然生成,而是人類為了農耕及放牧,非法放火燒林開闢土地所造成。

氣候變遷
國際新聞
巴西
亞馬遜雨林
大火

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※別再煩惱如何寫文案,掌握八大原則!

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※超省錢租車方案

※教你寫出一流的銷售文案?

網頁設計最專業,超強功能平台可客製化

※產品缺大量曝光嗎?你需要的是一流包裝設計!

蘇格蘭火車出軌意外 3人死亡、6人受傷

摘錄自2020年8月13日公視報導

日本連續一週的高溫已經造成10人死亡。韓國連下50多天的雨,也有42死或失聯。英國蘇格蘭東北部昨天則是發生一起嚴重的火車出軌意外,包括駕駛在內共3人死亡、6人受傷,由於事發當地連日豪雨引發土石災情,英國當局不排除出軌與天氣因素有關。

蘇格蘭東北部的「亞伯丁郡」12日上午9點43分發生列車出軌。這輛蘇格蘭鐵路的列車屬於雙車頭,並有四個車廂,原定終點站為「格拉斯哥皇后街」。不過卻在亞伯丁市區南邊15公里處的「斯冬希文鎮」出軌,當時車上9人,駕駛、調度員和一名乘客當場身亡,其餘六人受輕傷。

事發的斯冬希文鎮,近日來飽受水災所苦。當地媒體報導,可能是山崩引發這場意外,蘇格蘭首席大臣雖然不否認可能性,但希望交通警察部門能徹底調查原因。

國際新聞
蘇格蘭
火車

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※教你寫出一流的銷售文案?

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※回頭車貨運收費標準

※別再煩惱如何寫文案,掌握八大原則!

※超省錢租車方案

※產品缺大量曝光嗎?你需要的是一流包裝設計!

玻璃纖維船的全球污染 學者警告:大量棄置海上 威脅海洋生物生存

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

※產品缺大量曝光嗎?你需要的是一流包裝設計!

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

日本各地連日高溫 中暑送醫或死亡案例頻傳

摘錄自2020年8月13日中央社報導

日本各地連續幾天出現超過攝氏35度的「猛暑日」,甚至還有地方出現逾40度高溫,讓截至9日為止的一週內,共有超過6600人疑似中暑送醫,是前一週兩倍,並相繼傳出死亡案例。

日本讀賣新聞報導,截至今天(13日)中午為止,全日本至少有64處觀測點出現逾攝氏35度的「猛暑日」,日本氣象廳已針對關東地方發布「中暑警戒警報」。

以中午12時30分的各地最高氣溫來看,愛媛縣愛南町37.6度、群馬縣桐生市37.4度,埼玉縣鳩山町、靜岡縣川根本町及東京都等地,也都超過或達到37度。

神奈川縣昨天至少有76人疑似中暑送醫,其中有一人死亡、四人成為重症患者;愛知縣春日井市也有一名87歲男性在騎自行車時突然昏倒,疑似中暑,送醫後宣告不治,當時這名男性騎車有戴口罩。

愛知縣政府呼籲,在酷暑時期,可以適當地取下口罩,並避免在外活動,降低中暑風險。

氣候變遷
國際新聞
日本
高溫天氣
熱傷害

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※想知道最厲害的網頁設計公司"嚨底家"!

※別再煩惱如何寫文案,掌握八大原則!

※產品缺大量曝光嗎?你需要的是一流包裝設計!