標籤: 新北清潔

  • 挽救電動車產業 印度政府再次開啟投資

    挽救電動車產業 印度政府再次開啟投資

        印度政府日前通過一項 23 億美元的電動車產業振興計劃──2020 國家電動車行動計劃(NEMMP 2020 plan),打算重新帶起終止補貼後明顯下滑的電動車銷量。   印度政府過去對電動車的銷售補貼相當成功,全盛時期銷售量甚至超過 10 萬輛,但當新暨再生能源部 MNRE 在 2012 年 3 月停止補貼後,銷售量開始下滑,到了 2013 年,只剩 2.1 萬輛。幾年前,印度有 65 家電動車製造商,現在只剩 10家還在營運。   NEMMP 計劃目標是在 2020 年有 6 到 7 百萬輛電動車上路,而其中 4 到 5 百萬輛為二輪車,若達到該銷量數字,還能節省超過 200 萬噸燃料的使用。   雖然大多數電動車公司在過去兩年來已停止營運,但假如 NEMMP 2020 可在接下來的 3 或 4 個月間實施,預期印度電動車產業仍可復甦。     (圖片來源:)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

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

  • 美電動車銷量 7 月破萬持續看漲

    美國汽車媒體 GreenCarReports 報導,由於日產和通用兩家公司的聆風和雪佛蘭沃藍達在美國市場銷量表現不俗,美國 7 月插電式電動汽車銷量繼續看漲。聆風和雪佛蘭沃藍達的月度銷售量相加第二次超過 5000 台,上次月度銷量超過 5000 台是去年 8 月。    數據顯示,2014 年 7 月美國插電式電動車整體銷量為 1 萬 533 輛。其中日產聆風當月在美國本土市場的銷量為 3,019 輛,位居第一;其次是雪佛蘭沃藍達,銷量為 2,020 輛,取代了此前豐田普銳斯季軍的位置;豐田普銳斯當季銷量為 1,371 輛。   不難發現,美國新能源汽車市場的主要車型相對集中。日產的聆風、雪佛蘭沃藍達和豐田的普銳斯一直占據主導地位,也是美國傳統汽車品牌中新能源汽車領域的三大陣地。事實上,日產聆風電動車 7 月在美國市場的銷量並不是歷史最高銷量。其聆風電動車銷量的高峰是今年 5 月,共計銷售 3,117 輛,同比上漲 46%。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

    ※超省錢租車方案

  • Tesla 展霸氣!推「8 年不限里程保固」

    電動車大廠特斯拉(Tesla)近日再推霸氣新政策,根據馬斯克(Elon Musk)在官方部落格發表的文章,現在他們最受歡迎的車款 Model S 將享有 8 年不限里程的保固,雖然這項新措施對客戶非常大方,但馬斯克也表示這將對短期內的獲利有負面影響。   「如果我們真的相信電動機比傳統的汽油引擎可靠,而且有著更少的油質殘渣與廢氣,那我們的保固政策就該反映出這點。」馬斯克信心十足的說。另外,他在文章內也對投資者表示,雖然因為保修儲備必須提升到更高的檔次,這項政策短期內對公司的盈利會有負面影響,但長期來說,他相信只要及早做出正確的決定肯定會對公司有所幫助。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

    ※超省錢租車方案

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

  • 技術乾貨丨卷積神經網絡之LeNet-5遷移實踐案例

    技術乾貨丨卷積神經網絡之LeNet-5遷移實踐案例

    摘要:LeNet-5是Yann LeCun在1998年設計的用於手寫数字識別的卷積神經網絡,當年美國大多數銀行就是用它來識別支票上面的手寫数字的,它是早期卷積神經網絡中最有代表性的實驗系統之一。可以說,LeNet-5就相當於編程語言入門中的“Hello world!”。

    華為的昇騰訓練芯片一直是大家所期待的,目前已經開始提供公測,如何在昇騰訓練芯片上運行一個訓練任務,這是目前很多人都在采坑過程中,所以我寫了一篇指導文章,附帶上所有相關源代碼。注意,本文並沒有包含環境的安裝,請查看另外相關文檔。

    環境約束:昇騰910目前僅配套TensorFlow 1.15版本。

    基礎鏡像上傳之後,我們需要啟動鏡像命令,以下命令掛載了8塊卡(單機所有卡):

    docker run -it –net=host –device=/dev/davinci0 –device=/dev/davinci1 –device=/dev/davinci2 –device=/dev/davinci3 –device=/dev/davinci4 –device=/dev/davinci5 –device=/dev/davinci6 –device=/dev/davinci7 –device=/dev/davinci_manager –device=/dev/devmm_svm –device=/dev/hisi_hdc -v /var/log/npu/slog/container/docker:/var/log/npu/slog -v /var/log/npu/conf/slog/slog.conf:/var/log/npu/conf/slog/slog.conf -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ -v /usr/local/Ascend/driver/tools/:/usr/local/Ascend/driver/tools/ -v /data/:/data/ -v /home/code:/home/local/code -v ~/context:/cache ubuntu_18.04-docker.arm64v8:v2 /bin/bash

    設置環境變量並啟動手寫字訓練網絡:

    #!/bin/bash
    export LD_LIBRARY_PATH=/usr/local/lib/:/usr/local/HiAI/runtime/lib64
    export PATH=/usr/local/HiAI/runtime/ccec_compiler/bin:$PATH
    export CUSTOM_OP_LIB_PATH=/usr/local/HiAI/runtime/ops/framework/built-in/tensorflow
    export DDK_VERSION_PATH=/usr/local/HiAI/runtime/ddk_info
    export WHICH_OP=GEOP
    export NEW_GE_FE_ID=1
    export GE_AICPU_FLAG=1
    export OPTION_EXEC_EXTERN_PLUGIN_PATH=/usr/local/HiAI/runtime/lib64/plugin/opskernel/libfe.so:/usr/local/HiAI/runtime/lib64/plugin/opskernel/libaicpu_plugin.so:/usr/local/HiAI/runtime/lib64/plugin/opskernel/libge_local_engine.so:/usr/local/H
    iAI/runtime/lib64/plugin/opskernel/librts_engine.so:/usr/local/HiAI/runtime/lib64/libhccl.so
     
    export OP_PROTOLIB_PATH=/usr/local/HiAI/runtime/ops/built-in/
     
    export DEVICE_ID=2
    export PRINT_MODEL=1
    #export DUMP_GE_GRAPH=2
     
    #export DISABLE_REUSE_MEMORY=1
    #export DUMP_OP=1
    #export SLOG_PRINT_TO_STDOUT=1
     
    export RANK_ID=0
    export RANK_SIZE=1
    export JOB_ID=10087
    export OPTION_PROTO_LIB_PATH=/usr/local/HiAI/runtime/ops/op_proto/built-in/
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/Ascend/fwkacllib/lib64/:/usr/local/Ascend/driver/lib64/common/:/usr/local/Ascend/driver/lib64/driver/:/usr/local/Ascend/add-ons/
    export PYTHONPATH=$PYTHONPATH:/usr/local/Ascend/opp/op_impl/built-in/ai_core/tbe
    export PATH=$PATH:/usr/local/Ascend/fwkacllib/ccec_compiler/bin
    export ASCEND_HOME=/usr/local/Ascend
    export ASCEND_OPP_PATH=/usr/local/Ascend/opp
    export SOC_VERSION=Ascend910
     
    rm -f *.pbtxt
    rm -f *.txt
    rm -r /var/log/npu/slog/*.log
    rm -rf train_url/*
     
     
    python3 mnist_train.py

    以下訓練案例中我使用的lecun大師的LeNet-5網絡,先簡單介紹LeNet-5網絡:

    LeNet5誕生於1994年,是最早的卷積神經網絡之一,並且推動了深度學習領域的發展。自從1988年開始,在多年的研究和許多次成功的迭代后,這項由Yann LeCun完成的開拓性成果被命名為LeNet5。

    LeNet-5包含七層,不包括輸入,每一層都包含可訓練參數(權重),當時使用的輸入數據是32*32像素的圖像。下面逐層介紹LeNet-5的結構,並且,卷積層將用Cx表示,子採樣層則被標記為Sx,完全連接層被標記為Fx,其中x是層索引。

    層C1是具有六個5*5的卷積核的卷積層(convolution),特徵映射的大小為28*28,這樣可以防止輸入圖像的信息掉出卷積核邊界。C1包含156個可訓練參數和122304個連接。

    層S2是輸出6個大小為14*14的特徵圖的子採樣層(subsampling/pooling)。每個特徵地圖中的每個單元連接到C1中的對應特徵地圖中的2*2個鄰域。S2中單位的四個輸入相加,然後乘以可訓練係數(權重),然後加到可訓練偏差(bias)。結果通過S形函數傳遞。由於2*2個感受域不重疊,因此S2中的特徵圖只有C1中的特徵圖的一半行數和列數。S2層有12個可訓練參數和5880個連接。

    層C3是具有16個5-5的卷積核的卷積層。前六個C3特徵圖的輸入是S2中的三個特徵圖的每個連續子集,接下來的六個特徵圖的輸入則來自四個連續子集的輸入,接下來的三個特徵圖的輸入來自不連續的四個子集。最後,最後一個特徵圖的輸入來自S2所有特徵圖。C3層有1516個可訓練參數和156 000個連接。

    層S4是與S2類似,大小為2*2,輸出為16個5*5的特徵圖。S4層有32個可訓練參數和2000個連接。

    層C5是具有120個大小為5*5的卷積核的卷積層。每個單元連接到S4的所有16個特徵圖上的5*5鄰域。這裏,因為S4的特徵圖大小也是5*5,所以C5的輸出大小是1*1。因此S4和C5之間是完全連接的。C5被標記為卷積層,而不是完全連接的層,是因為如果LeNet-5輸入變得更大而其結構保持不變,則其輸出大小會大於1*1,即不是完全連接的層了。C5層有48120個可訓練連接。

    F6層完全連接到C5,輸出84張特徵圖。它有10164個可訓練參數。這裏84與輸出層的設計有關。

    LeNet的設計較為簡單,因此其處理複雜數據的能力有限;此外,在近年來的研究中許多學者已經發現全連接層的計算代價過大,而使用全部由卷積層組成的神經網絡。

    LeNet-5網絡訓練腳本是mnist_train.py,具體代碼:

    import os
    import numpy as np
    import tensorflow as tf
    import time
    from tensorflow.examples.tutorials.mnist import input_data
     
    import mnist_inference
     
    from npu_bridge.estimator import npu_ops #導入NPU算子庫
    from tensorflow.core.protobuf.rewriter_config_pb2 import RewriterConfig #重寫tensorFlow里的配置,針對NPU的配置
     
     
    batch_size = 100
    learning_rate = 0.1
    training_step = 10000
     
    model_save_path = "./model/"
    model_name = "model.ckpt"
     
    def train(mnist):
        x = tf.placeholder(tf.float32, [batch_size, mnist_inference.image_size, mnist_inference.image_size, mnist_inference.num_channels], name = 'x-input')
        y_ = tf.placeholder(tf.float32, [batch_size, mnist_inference.num_labels], name = "y-input")
     
        regularizer = tf.contrib.layers.l2_regularizer(0.001)
        y = mnist_inference.inference(x, train = True, regularizer = regularizer) #推理過程
        global_step = tf.Variable(0, trainable=False)
        cross_entropy = tf.nn.sparse_softmax_cross_entropy_with_logits(logits = y, labels = tf.argmax(y_, 1)) #損失函數
        cross_entropy_mean = tf.reduce_mean(cross_entropy)
        loss = cross_entropy_mean + tf.add_n(tf.get_collection("loss"))
     
        train_step = tf.train.GradientDescentOptimizer(learning_rate).minimize(loss, global_step = global_step) #優化器調用
     
        saver = tf.train.Saver() #啟動訓練
     
    #以下代碼是NPU所必須的代碼,開始配置參數
        config = tf.ConfigProto(
            allow_soft_placement = True,
            log_device_placement = False)
        custom_op =  config.graph_options.rewrite_options.custom_optimizers.add()
        custom_op.name =  "NpuOptimizer"
        custom_op.parameter_map["use_off_line"].b = True
        #custom_op.parameter_map["profiling_mode"].b = True
        #custom_op.parameter_map["profiling_options"].s = tf.compat.as_bytes("task_trace:training_trace")
    config.graph_options.rewrite_options.remapping = RewriterConfig.OFF
    #配置參數結束
     
        writer = tf.summary.FileWriter("./log_dir", tf.get_default_graph())
        writer.close()
     
     
    #參數初始化
        with tf.Session(config = config) as sess:
            tf.global_variables_initializer().run()
     
            start_time = time.time()
     
            for i in range(training_step):
    xs, ys = mnist.train.next_batch(batch_size)
                reshaped_xs = np.reshape(xs, (batch_size, mnist_inference.image_size, mnist_inference.image_size, mnist_inference.num_channels))
                _, loss_value, step = sess.run([train_step, loss, global_step], feed_dict={x:reshaped_xs, y_:ys})
     
                #每訓練10個epoch打印損失函數輸出日誌
                if i % 10 == 0:
                    print("****************************++++++++++++++++++++++++++++++++*************************************\n" * 10)
                    print("After %d training steps, loss on training batch is %g, total time in this 1000 steps is %s." % (step, loss_value, time.time() - start_time))
                    #saver.save(sess, os.path.join(model_save_path, model_name), global_step = global_step)
                    print("****************************++++++++++++++++++++++++++++++++*************************************\n" * 10)
                    start_time = time.time()
    def main():
        mnist = input_data.read_data_sets('MNIST_DATA/', one_hot= True)
        train(mnist)
     
    if __name__ == "__main__":
        main()

    本文主要講述了經典卷積神經網絡之LeNet-5網絡模型和遷移至昇騰D910的實現,希望大家快來動手操作一下試試看!

     

    點擊關注,第一時間了解華為雲新鮮技術~

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 新來的”大神”用策略模式把if else給”優化”了,技術總監說:能不能想好了再改?

    新來的”大神”用策略模式把if else給”優化”了,技術總監說:能不能想好了再改?

    本文來自作者投稿,原作者:上帝愛吃蘋果

    目前在魔都,貝殼找房是我的僱主,平時關注一些 java 領域相關的技術,希望你們能在這篇文章中找到些有用的東西。個人水平有限,如果文章有錯誤還請指出,在留言區一起交流。

    我想大家肯定都或多或少的看過各種“策略模式”的講解、佈道等等,這篇文章就是來好好“澄清”一下策略模式,並嘗試回答以下的問題:

    1. 策略模式是如何優化業務邏輯代碼結構的?

    2. 殺雞焉用宰牛刀?就是幾個if else場景我需要用到策略模式?

    3. 有沒有什麼更好的代碼結構來實現策略模式的嗎?

    策略模式是如何優化業務邏輯代碼結構的?

    要回答這個問題,我們還得先扒一扒策略模式的定義,從定義着手來理解它

    策略模式的教科書定義

    它的定義很精簡:一個類的行為或其算法可以在運行時更改。我們把它降維到代碼層面,用人話翻譯一下就是,運行時我給你這個類的方法傳不同的“key”,你這個方法會執行不同的業務邏輯。細品一下,這不就是 if else 乾的事嗎?

    策略模式優化了什麼?

    其實策略模式的核心思想和 if else如出一轍,根據不同的key動態的找到不同的業務邏輯,那它就只是如此嗎?

    實際上,我們口中的策略模式其實就是在代碼結構上調整,用接口+實現類+分派邏輯來使代碼結構可維護性好點。

    一般教科書上講解到接口與實現類就結束了,其他博客上會帶上提及分派邏輯。這裏就不啰嗦了。

    小結一下,即使用了策略模式,你該寫的業務邏輯照常寫,到邏輯分派的時候,還是變相的if else。而它的優化點是抽象了出了接口,將業務邏輯封裝成一個一個的實現類,任意地替換。在複雜場景(業務邏輯較多)時比直接 if else 來的好維護些。

    殺雞焉用宰牛刀?就是幾個if else場景我需要用到策略模式?!

    我想小夥伴們經常有這樣的不滿,我的業務邏輯就3 4 行,你給我整一大堆類定義?有必要這麼麻煩嗎?我看具體的業務邏輯還需要去不同的類中,簡單點行不行。

    其實我們所不滿的就是策略模式帶來的缺點:

    1、策略類會增多

    2、業務邏輯分散到各個實現類中,而且沒有一個地方可以俯視整個業務邏輯

    針對傳統策略模式的缺點,在這分享一個實現思路,這個思路已經幫我們團隊解決了多個複雜if else的業務場景,理解上比較容易,代碼上需要用到Java8的特性——利用Map與函數式接口來實現。

    直接show代碼結構:為了簡單演示一個思路,代碼用String 類型來模擬一個業務BO

    其中:

    1. getCheckResult() 為傳統的做法

    2. getCheckResultSuper()則事先在Map中定義好了“判斷條件”與“業務邏輯”的映射關係,具體講解請看代碼註釋

    /**
     * 某個業務服務類
     */
    @Service
    public class BizService {
    
        /**
         * 傳統的 if else 解決方法
         * 當每個業務邏輯有 3 4 行時,用傳統的策略模式不值得,直接的if else又顯得不易讀
         */
        public String getCheckResult(String order) {
            if ("校驗1".equals(order)) {
                return "執行業務邏輯1";
            } else if ("校驗2".equals(order)) {
                return "執行業務邏輯2";
            }else if ("校驗3".equals(order)) {
                return "執行業務邏輯3";
            }else if ("校驗4".equals(order)) {
                return "執行業務邏輯4";
            }else if ("校驗5".equals(order)) {
                return "執行業務邏輯5";
            }else if ("校驗6".equals(order)) {
                return "執行業務邏輯6";
            }else if ("校驗7".equals(order)) {
                return "執行業務邏輯7";
            }else if ("校驗8".equals(order)) {
                return "執行業務邏輯8";
            }else if ("校驗9".equals(order)) {
                return "執行業務邏輯9";
            }
            return "不在處理的邏輯中返回業務錯誤";
        }
    
        /**
         * 業務邏輯分派Map
         * Function為函數式接口,下面代碼中 Function<String, String> 的含義是接收一個Stirng類型的變量,返回一個String類型的結果
         */
        private Map<String, Function<String, String>> checkResultDispatcher = new HashMap<>();
    
        /**
         * 初始化 業務邏輯分派Map 其中value 存放的是 lambda表達式
         */
        @PostConstruct
        public void checkResultDispatcherInit() {
            checkResultDispatcher.put("校驗1", order -> String.format("對%s執行業務邏輯1", order));
            checkResultDispatcher.put("校驗2", order -> String.format("對%s執行業務邏輯2", order));
            checkResultDispatcher.put("校驗3", order -> String.format("對%s執行業務邏輯3", order));
            checkResultDispatcher.put("校驗4", order -> String.format("對%s執行業務邏輯4", order));
            checkResultDispatcher.put("校驗5", order -> String.format("對%s執行業務邏輯5", order));
            checkResultDispatcher.put("校驗6", order -> String.format("對%s執行業務邏輯6", order));
            checkResultDispatcher.put("校驗7", order -> String.format("對%s執行業務邏輯7", order));
            checkResultDispatcher.put("校驗8", order -> String.format("對%s執行業務邏輯8", order));
            checkResultDispatcher.put("校驗9", order -> String.format("對%s執行業務邏輯9", order));
        }
    
        public String getCheckResultSuper(String order) {
            //從邏輯分派Dispatcher中獲得業務邏輯代碼,result變量是一段lambda表達式
            Function<String, String> result = checkResultDispatcher.get(order);
            if (result != null) {
                //執行這段表達式獲得String類型的結果
                return result.apply(order);
            }
            return "不在處理的邏輯中返回業務錯誤";
        }
    }
    

    通過http調用一下看看效果:

    /**
     * 模擬一次http調用
     */
    @RestController
    public class BizController {
    
        @Autowired
        private BizService bizService;
    
        @PostMapping("/v1/biz/testSuper")
        public String test2(String order) {
            return bizService.getCheckResultSuper(order);
        }
    }
    

    這是個簡單的demo演示,之後會舉一些複雜的場景案例。

    魯迅曾說過,“每解決一個問題,就會因出更多的問題”。我們一起來看看這樣的實現有什麼好處,會帶來什麼問題。

    好處很直觀:

    1. 在一段代碼里直觀的看到”判斷條件”與業務邏輯的映射關係
    2. 不需要單獨定義接口與實現類,直接使用現有的函數式接口(什麼?不知道函數式接口?快去了解),而實現類直接就是業務代碼本身。

    不好的點:

    1. 需要團隊成員對lambda表達式有所了解(什麼?Java14都出來了還有沒用上Java8新特性的小夥伴?)

    接下來我舉幾個在業務中經常遇到的if else場景,並用Map+函數式接口的方式來解決它

    在真實業務場景問題的解決思路

    有的小夥伴會說,我的判斷條件有多個啊,而且很複雜,你之前舉個例子只有單個判斷邏輯,而我有多個判斷邏輯該怎麼辦呢?

    很好解決:寫一個判斷邏輯的方法,Map的key由方法計算出

    /**
     * 某個業務服務類
     */
    @Service
    public class BizService {
    
        private Map<String, Function<String, String>> checkResultDispatcherMuti = new HashMap<>();
    
        /**
         * 初始化 業務邏輯分派Map 其中value 存放的是 lambda表達式
         */
        @PostConstruct
        public void checkResultDispatcherMuitInit() {
            checkResultDispatcherMuti.put("key_訂單1", order -> String.format("對%s執行業務邏輯1", order));
            checkResultDispatcherMuti.put("key_訂單1_訂單2", order -> String.format("對%s執行業務邏輯2", order));
            checkResultDispatcherMuti.put("key_訂單1_訂單2_訂單3", order -> String.format("對%s執行業務邏輯3", order));
        }
    
        public String getCheckResultMuti(String order, int level) {
            //寫一段生成key的邏輯:
            String ley = getDispatcherKey(order, level);
    
            Function<String, String> result = checkResultDispatcherMuti.get(ley);
            if (result != null) {
                //執行這段表達式獲得String類型的結果
                return result.apply(order);
            }
            return "不在處理的邏輯中返回業務錯誤";
        }
    
        /**
         * 判斷條件方法
         */
        private String getDispatcherKey(String order, int level) {
            StringBuilder key = new StringBuilder("key");
            for (int i = 1; i <= level; i++) {
                key.append("_" + order + i);
            }
            return key.toString();
        }
    }
    

    用http模擬一下:

    /**
     * 模擬一次http調用
     */
    @RestController
    public class BizController {
    
        @Autowired
        private BizService bizService;
    
        @PostMapping("/v1/biz/testMuti")
        public String test1(String order, Integer level) {
            return bizService.getCheckResultMuti(order, level);
        }
    }
    

    只要設計好你的key的生成規則就好。

    還有小夥伴會問:我的業務邏輯有很多很多行,在checkResultDispatcherMuitInit()方法的Map中直接寫不會很長嗎?

    直接寫當然長了,我們可以抽象出一個service服務專門放業務邏輯,然後在定義中調用它就好了:

    提供一個業務邏輯單元:

    /**
     * 提供業務邏輯單元
     */
    @Service
    public class BizUnitService {
    
        public String bizOne(String order) {
            return order + "各種花式操作1";
        }
        public String bizTwo(String order) {
            return order + "各種花式操作2";
        }
        public String bizThree(String order) {
            return order + "各種花式操作3";
        }
    }
    
    /**
     * 某個業務服務類
     */
    @Service
    public class BizService {
        @Autowired
        private BizUnitService bizUnitService;
    
        private Map<String, Function<String, String>> checkResultDispatcherComX = new HashMap<>();
    
        /**
         * 初始化 業務邏輯分派Map 其中value 存放的是 lambda表達式
         */
        @PostConstruct
        public void checkResultDispatcherComXInit() {
            checkResultDispatcherComX.put("key_訂單1", order -> bizUnitService.bizOne(order));
            checkResultDispatcherComX.put("key_訂單1_訂單2", order -> bizUnitService.bizTwo(order));
            checkResultDispatcherComX.put("key_訂單1_訂單2_訂單3", order -> bizUnitService.bizThree(order));
        }
    
        public String getCheckResultComX(String order, int level) {
            //寫一段生成key的邏輯:
            String ley = getDispatcherComXKey(order, level);
    
            Function<String, String> result = checkResultDispatcherComX.get(ley);
            if (result != null) {
                //執行這段表達式獲得String類型的結果
                return result.apply(order);
            }
            return "不在處理的邏輯中返回業務錯誤";
        }
    
        /**
         * 判斷條件方法
         */
        private String getDispatcherComXKey(String order, int level) {
            StringBuilder key = new StringBuilder("key");
            for (int i = 1; i <= level; i++) {
                key.append("_" + order + i);
            }
            return key.toString();
        }
    }
    

    調用結果:

    總結

    最後,我們一起嘗試回答以下幾個問題:

    1. 策略模式是如何優化業務邏輯代碼結構的?

    抽象了出了接口,將業務邏輯封裝成一個一個的實現類,任意地替換。在複雜場景(業務邏輯較多)時比直接 if else 來的好維護些。

    2. 殺雞焉用宰牛刀?就是幾個if else場景我需要用到策略模式?!

    我們所不滿的其實就是傳統接口實現的缺點: 1、策略類會很多。 2、業務邏輯分散到各個實現類中,而且沒有一個地方可以俯覽整個業務邏輯

    3. 有沒有什麼更好的代碼結構來實現策略模式的嗎?

    針對傳統策略模式的缺點,分享了利用Map與函數式接口來實現的思路。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理
    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※回頭車貨運收費標準

  • Spark學習筆記(三)-Spark Streaming

    Spark學習筆記(三)-Spark Streaming

    Spark Streaming支持實時數據流的可擴展(scalable)、高吞吐(high-throughput)、容錯(fault-tolerant)的流處理(stream processing)。

     

                                                        架構圖

     

    特性如下:

     

    • 可線性伸縮至超過數百個節點;

    • 實現亞秒級延遲處理;

    • 可與Spark批處理和交互式處理無縫集成;

    • 提供簡單的API實現複雜算法;

    • 更多的流方式支持,包括Kafka、Flume、Kinesis、Twitter、ZeroMQ等。

     

    原理

     

    Spark在接收到實時輸入數據流后,將數據劃分成批次(divides the data into batches),然後轉給Spark Engine處理,按批次生成最後的結果流(generate the final stream of results in batches)。 

     

     

    API

     

    DStream

     

    DStream(Discretized Stream,離散流)是Spark Stream提供的高級抽象連續數據流。

     

    • 組成:一個DStream可看作一個RDDs序列。

    • 核心思想:將計算作為一系列較小時間間隔的、狀態無關的、確定批次的任務,每個時間間隔內接收的輸入數據被可靠存儲在集群中,作為一個輸入數據集。

     

     

    • 特性:一個高層次的函數式編程API、強一致性以及高校的故障恢復。

    • 應用程序模板:

    • 模板1

    • 模板2

     

    WordCount示例

     

     

    Input DStream

     

    Input DStream是一種從流式數據源獲取原始數據流的DStream,分為基本輸入源(文件系統、Socket、Akka Actor、自定義數據源)和高級輸入源(Kafka、Flume等)。

     

    • Receiver:
    • 每個Input DStream(文件流除外)都會對應一個單一的Receiver對象,負責從數據源接收數據並存入Spark內存進行處理。應用程序中可創建多個Input DStream并行接收多個數據流。

    • 每個Receiver是一個長期運行在Worker或者Executor上的Task,所以會佔用該應用程序的一個核(core)。如果分配給Spark Streaming應用程序的核數小於或等於Input DStream個數(即Receiver個數),則只能接收數據,卻沒有能力全部處理(文件流除外,因為無需Receiver)。

    • Spark Streaming已封裝各種數據源,需要時參考官方文檔。

     

    Transformation Operation

     

    • 常用Transformation

     

    * map(func) :對源DStream的每個元素,採用func函數進行轉換,得到一個新的DStream;

    * flatMap(func):與map相似,但是每個輸入項可用被映射為0個或者多個輸出項;

    * filter(func):返回一個新的DStream,僅包含源DStream中滿足函數func的項;

    * repartition(numPartitions):通過創建更多或者更少的分區改變DStream的并行程度;

    * union(otherStream):返回一個新的DStream,包含源DStream和其他DStream的元素;

    * count():統計源DStream中每個RDD的元素數量;

    * reduce(func):利用函數func聚集源DStream中每個RDD的元素,返回一個包含單元素RDDs的新DStream;

    * countByValue():應用於元素類型為K的DStream上,返回一個(K,V)鍵值對類型的新DStream,每個鍵的值是在原DStream的每個RDD中的出現次數;

    * reduceByKey(func, [numTasks]):當在一個由(K,V)鍵值對組成的DStream上執行該操作時,返回一個新的由(K,V)鍵值對組成的DStream,每一個key的值均由給定的recuce函數(func)聚集起來;

    * join(otherStream, [numTasks]):當應用於兩個DStream(一個包含(K,V)鍵值對,一個包含(K,W)鍵值對),返回一個包含(K, (V, W))鍵值對的新DStream;

    * cogroup(otherStream, [numTasks]):當應用於兩個DStream(一個包含(K,V)鍵值對,一個包含(K,W)鍵值對),返回一個包含(K, Seq[V], Seq[W])的元組;

    * transform(func):通過對源DStream的每個RDD應用RDD-to-RDD函數,創建一個新的DStream。支持在新的DStream中做任何RDD操作。

     

    • updateStateByKey(func)

    • updateStateByKey可對DStream中的數據按key做reduce,然後對各批次數據累加

    • WordCount的updateStateByKey版本

     

    • transform(func)

    • 通過對原DStream的每個RDD應用轉換函數,創建一個新的DStream。

    • 官方文檔代碼舉例

     

    • Window operations

    • 窗口操作:基於window對數據transformation(個人認為與Storm的tick相似,但功能更強大)。

    • 參數:窗口長度(window length)和滑動時間間隔(slide interval)必須是源DStream批次間隔的倍數。

    • 舉例說明:窗口長度為3,滑動時間間隔為2;上一行是原始DStream,下一行是窗口化的DStream。

    • 常見window operation

    有狀態轉換包括基於滑動窗口的轉換和追蹤狀態變化(updateStateByKey)的轉換。

    基於滑動窗口的轉換

    * window(windowLength, slideInterval) 基於源DStream產生的窗口化的批數據,計算得到一個新的DStream;

    * countByWindow(windowLength, slideInterval) 返迴流中元素的一個滑動窗口數;

    * reduceByWindow(func, windowLength, slideInterval) 返回一個單元素流。利用函數func聚集滑動時間間隔的流的元素創建這個單元素流。函數func必須滿足結合律,從而可以支持并行計算;

    * reduceByKeyAndWindow(func, windowLength, slideInterval, [numTasks]) 應用到一個(K,V)鍵值對組成的DStream上時,會返回一個由(K,V)鍵值對組成的新的DStream。每一個key的值均由給定的reduce函數(func函數)進行聚合計算。注意:在默認情況下,這個算子利用了Spark默認的併發任務數去分組。可以通過numTasks參數的設置來指定不同的任務數;

    * reduceByKeyAndWindow(func, invFunc, windowLength, slideInterval, [numTasks]) 更加高效的reduceByKeyAndWindow,每個窗口的reduce值,是基於先前窗口的reduce值進行增量計算得到的;它會對進入滑動窗口的新數據進行reduce操作,並對離開窗口的老數據進行“逆向reduce”操作。但是,只能用於“可逆reduce函數”,即那些reduce函數都有一個對應的“逆向reduce函數”(以InvFunc參數傳入);

    * countByValueAndWindow(windowLength, slideInterval, [numTasks]) 當應用到一個(K,V)鍵值對組成的DStream上,返回一個由(K,V)鍵值對組成的新的DStream。每個key的值都是它們在滑動窗口中出現的頻率。

    • 官方文檔代碼舉例 

     

    • join(otherStream, [numTasks])

    • 連接數據流

    • 官方文檔代碼舉例1

    • 官方文檔代碼舉例2

     

    Output Operation

     

     

    緩存與持久化

     

    • 通過persist()將DStream中每個RDD存儲在內存。

    • Window operations會自動持久化在內存,無需显示調用persist()。

    • 通過網絡接收的數據流(如Kafka、Flume、Socket、ZeroMQ、RocketMQ等)執行persist()時,默認在兩個節點上持久化序列化后的數據,實現容錯。

     

    Checkpoint

     

    • 用途:Spark基於容錯存儲系統(如HDFS、S3)進行故障恢復。

    • 分類:

    • 元數據檢查點:保存流式計算信息用於Driver運行節點的故障恢復,包括創建應用程序的配置、應用程序定義的DStream operations、已入隊但未完成的批次。

    • 數據檢查點:保存生成的RDD。由於stateful transformation需要合併多個批次的數據,即生成的RDD依賴於前幾個批次RDD的數據(dependency chain),為縮短dependency chain從而減少故障恢復時間,需將中間RDD定期保存至可靠存儲(如HDFS)。

    • 使用時機:

    • Stateful transformation:updateStateByKey()以及window operations。

    • 需要Driver故障恢復的應用程序。

    • 使用方法

    • Stateful transformation

    streamingContext.checkpoint(checkpointDirectory)

     

    • 需要Driver故障恢復的應用程序(以WordCount舉例):如果checkpoint目錄存在,則根據checkpoint數據創建新StreamingContext;否則(如首次運行)新建StreamingContext。

     

    • checkpoint時間間隔

    • 方法:

    dstream.checkpoint(checkpointInterval)

     

    • 原則:一般設置為滑動時間間隔的5-10倍。

    • 分析:checkpoint會增加存儲開銷、增加批次處理時間。當批次間隔較小(如1秒)時,checkpoint可能會減小operation吞吐量;反之,checkpoint時間間隔較大會導致lineage和task數量增長。

     

    性能調優

     

    降低批次處理時間

     

    • 數據接收并行度

    • 增加DStream:接收網絡數據(如Kafka、Flume、Socket等)時會對數據反序列化再存儲在Spark,由於一個DStream只有Receiver對象,如果成為瓶頸可考慮增加DStream。

    • 設置“spark.streaming.blockInterval”參數:接收的數據被存儲在Spark內存前,會被合併成block,而block數量決定了Task數量;舉例,當批次時間間隔為2秒且block時間間隔為200毫秒時,Task數量約為10;如果Task數量過低,則浪費了CPU資源;推薦的最小block時間間隔為50毫秒。

    • 顯式對Input DStream重新分區:在進行更深層次處理前,先對輸入數據重新分區。

    inputStream.repartition(<number of partitions>)

     

    • 數據處理并行度:reduceByKey、reduceByKeyAndWindow等operation可通過設置“spark.default.parallelism”參數或顯式設置并行度方法參數控制。

    • 數據序列化:可配置更高效的Kryo序列化。

     

    設置合理批次時間間隔

     

    • 原則:處理數據的速度應大於或等於數據輸入的速度,即批次處理時間大於或等於批次時間間隔。

    • 方法:

    • 先設置批次時間間隔為5-10秒以降低數據輸入速度;

    • 再通過查看log4j日誌中的“Total delay”,逐步調整批次時間間隔,保證“Total delay”小於批次時間間隔。

     

    內存調優

     

    • 持久化級別:開啟壓縮,設置參數“spark.rdd.compress”。

    • GC策略:在Driver和Executor上開啟CMS。

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

    【其他文章推薦】

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

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

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

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

  • 只看到了別人28歲從字節跳動退休,背後的期權知識你知道嗎?

    只看到了別人28歲從字節跳動退休,背後的期權知識你知道嗎?

    前一陣字節跳動程序員,年僅 28 歲的郭宇財務自由,宣布退休的消息在網上火了一把,這個事情大家應該都知道了,細節就不多說了。

    我剛看了這個新聞之後,起初內心出現的幾個關鍵詞是:卧槽、牛、羡慕……在意識到瞎感慨、羡慕沒啥用之後,我心裏不禁有個疑問:他的錢是怎麼來的?

    我找在字節上班的朋友打聽了一下,據說郭同學近幾年投資賺了錢。當然還有網上大部分傳的,郭同學的發家致富是因為有字節跳動的期權,期權的價值隨着字節的估值翻上了天。

    說到期權,估計很多人對期權不太清楚,除了關注熱點,我們還得學點知識對不對。今天我就和大家聊聊期權,省的將來大家遇到類似機會了,你一臉懵逼,甚至掉坑裡。

    同時,因為我不是專業的,只是接觸過,所以講的不對、不嚴謹的,大家多擔待。

    講期權之前,我們先接着上面說完郭同學的期權。

    郭同學 2014 年加入字節,當時字節估值 5 億美元,現在字節估值 1000 億美元以上,估值妥妥的漲了 200 倍啊。如果當時有 10 萬期權,現在值 2000 萬;如果有 100 萬期權,現在值 2 億。就算考慮上後幾輪字節融資帶來的稀釋,打個折扣,那也是一筆不小的財富啊。

    那麼郭同學有多少期權呢?說實話我也不知道,引用幾年前他和同學的對話,大家自己猜測吧。

    郭同學的事說完了,我們繼續說期權。

    1. 什麼是期權?

    先舉個例子:

    比如公司承諾你說,4 年之後,可以按每股 10 塊錢的價格,購買公司 1 萬份股票。期權的“期”就是指 4 年時間;期權的“權”就是 10 塊錢購買 1 萬份股票的權利。

    等 4 年之後,如果公司的股票價格漲到了 100 塊,你依然可以用 10 塊錢的價格購買 1 萬份股票。花 10 萬買的股票就值 100 萬了,轉手賣出去之後,你能賺 90 萬。恭喜你!

    另外一種可能是,如果 4 年後公司股票價格掉到 8 塊錢,這時候你肯定就不會按 10 塊錢價格購買股票了,不行使這個權利你也不會虧錢。

    期權在國內互聯網公司不算少見,這當初是從硅谷那些互聯網公司學來的。

    公司為了留住你,同時也為了長期激勵你,期權通常不會一下都給你,按上面的例子來說,4 年時間,可能每滿一年給你 1/4 數量的期權,或者是按月給。這也是為什麼有人把期權稱為“金手銬”。

    2. 離職了期權怎麼辦?

    上面的例子繼續展開說,如果乾滿了兩年,還沒幹滿四年,離職了期權怎麼辦?

    這種情況,需要看之前你和公司怎麼約定的。通常有 3 種方式:

    1. 干滿 2 年,給了 50%,那麼就可以對這 50% 的期權行權,然後帶走,等着公司上市后變成股票。
    2. 公司回購你這 50% 的期權。
    3. 被公司收回,什麼都沒有。

    說一個我自己的例子,我在 2008 年打工的時候,公司給過我期權,數量不多,具體的價格、時間已經完全記不住了,後來離職的時候,期權都被公司收回了。當時對期權完全不懂,想着收回就收回唄。後來那家公司發展的不是很好,即使我堅守到現在,那些期權啥也兌現不了。否則,我現在腸子都要悔青了。

    3. 有期權是不是肯定拿低工資?

    這個不一定。比如小米早期,就有高工資低期權、低工資高期權兩種方案供員工選擇。

    如果你覺得老闆靠譜、公司有希望,同時少拿點工資也不影響自己的生活,當然可以選擇低工資高期權。博一把,一旦成了,那麼期權帶來的財富肯定遠超過工資。

    當然選擇高工資低期權,也能理解,畢竟生活不易,很多人都要養家糊口。而且創業公司的成功率非常低,期權最後能兌現都是小概率事件。萬一期權不能兌現,除了損失了工資,時間成本和機會成本也不小。

    但是大家要小心,有的公司會用期權給你畫個大大的餅,刻意把你的工資壓到非常低。如果老闆真有誠意,起碼會給你開一個可接受的工資。

    期權是為了留住和激勵優秀員工的,工資太低,優秀員工的忠誠度、穩定性怎麼保證?

    4. 白紙黑字

    如果你遇到的老闆是天天把分期權掛在嘴上,但是遲遲不和你簽協議,那你要小心了。無論怎樣,任何口頭承諾都沒有任何意義。

    一定要白紙黑字落在協議上,特別是行權時間、價格、數量、期限、公司回購價格等等要寫到合同協議里,省的將來產生糾紛。

    當然,最省心的還是找一個靠譜的公司,比如:

    1. 公司的創始人信用好,如果他之前兌現過期權,那就更好了。
    2. 是著名投資機構投資的公司。
    3. 過往的融資都很順利,而且每一輪融資公司估值都漲的很快。
    4. 能吸引很多優秀的人才加入,也能側面說明這家公司比較健康。大家都眼瞎的概率還是很小的。

    寫完以上幾點,我不禁想到了曾經的樂視,好像能滿足以上好幾點……

    5. 期權的其他知識

    進入公司差一年,為什麼期權相差很多?
    拿字節跳動來說,入職時間差一年,可能期權差一個數量級。發展越迅猛的公司,這個差距越大,早期公司估值低,越早加入越有優勢。

    期權怎麼變現?
    通常除了公司回購,只能等上市了。

    員工是否可以賣掉期權?
    一般來說,員工是不能出售期權的。如果員工可以出售期權,只能賣給你的公司。

    6. 乾股

    再說一下乾股。

    有些技術大牛在加入創業公司的時候,創始人可能會對技術大牛說:你出技術,不用出錢了,我白給你 20% 的乾股(或叫技術股)。

    乾股聽起來好像挺好,大家一起干,不用出錢,還可以分紅。

    但是通常乾股是虛股,只有分紅的權利,沒有參与經營、決策的權利,往往也不能轉讓。

    乾股是不規範的叫法,由於技術合伙人沒有實際出資,也沒經過工商登記,有可能忙活半天,最後你的名字都不會出現在股東名單里。如果之前也沒簽過協議,這麼不清不楚的,將來會不會被踢出局,都不好說。

    最穩妥的辦法還是去工商局登記變更,把乾股變成法律上認可的股權。

    最後

    以上就是這篇文章的主要內容,估計很多讀者會覺得,期權、股票這些離自己太遠,和自己關係不大。

    千萬別這麼想!

    第一、現在互聯網行業給股票、期權的公司不算少見,提前儲備點知識,萬一以後談 offer 的時候出現這些字眼,你也不至於啥也不懂。

    第二、覺得自己現在還很菜,公司的股票、期權都是給核心員工的,輪不到自己。現在菜又怎樣,誰不是由菜變強的,現在沒有股票、期權,不代表以後也拿不到。

    第三、在工資不降低或者可接受的範圍內,我建議大家拿股票、期權,萬一哪天自己就莫名其妙的發財了呢。

    第三點大家一定要謹慎,再次提醒一下你們,別為了股票、期權就接受一個特別低的工資,這塊我教訓深刻。

    就寫這麼多吧,希望這篇掃盲文章能讓你們有收穫。起碼我旁邊的碼農二毛又有收穫了:

    二毛說:四哥你這麼一說,我明白了,我現在就算能跳槽進字節,就算給我期權,現在太晚了,字節估值都這麼高了,別說暴富,估計連湯都喝不上了。我應該怎麼辦呢?

    我說:打開你的炒股軟件……

    二毛說:我明白了,四哥你是讓我買字節的股票,拿着股票也可以跟着字節市值上漲賺錢。

    我說:呸,字節還沒上市,你買毛線。我的意思是,如果幾年前你能拿出來選股票的勁頭來分析、選擇出一家潛力公司,早跳槽過去堅持到現在,估計你也發財了。

    感謝閱讀,如果覺得有用,希望你可以分享給你的朋友、同事,或者點個在看支持一下。

    —END—

    我準備了一些純手打的高質量PDF,有好友贊助的也有我自己的,大家可以免費領取:

    深入淺出Java多線程、HTTP超全匯總、Java基礎核心總結、程序員必知的硬核知識大全、簡歷面試談薪的超全乾貨。

    別看數量不多,但篇篇都是乾貨,看完的都說很肝。

    領取方式:掃碼關注后,在公眾號後台回復:666

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

    【其他文章推薦】

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

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

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

    ※幫你省時又省力,新北清潔一流服務好口碑

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

  • 微服務中如何設計一個權限授權服務

    微服務中如何設計一個權限授權服務

    基於角色的訪問控制  (RBAC) 

      是將系統訪問限製為授權用戶的一種方法,是圍繞角色和特權定義的與策略無關的訪問控制機制,RBAC的組件使執行用戶分配變得很簡單。

      在組織內部,將為各種職務創建角色執行某些操作的權限已分配給特定角色。成員或職員(或其他系統用戶)被分配了特定角色,並且通過這些角色分配獲得執行特定系統功能所需的權限。由於未直接為用戶分配權限,而是僅通過其角色(一個或多個角色)獲取權限,因此,對單個用戶權限的管理就變成了簡單地為用戶帳戶分配適當角色的問題。這簡化了常見操作,例如添加用戶或更改用戶部門。

    RBAC定義了三個主要規則

      1、角色分配:僅當對象已選擇或分配了角色時,對象才能行使權限。

      2、角色授權:必須為主體授權主體的活動角色。使用上面的規則1,此規則可確保用戶只能承擔獲得其授權的角色。

      3、權限授權:僅當對象的活動角色被授權時,對象才能行使權限。使用規則1和2,此規則可確保用戶只能行使其被授權的權限。

    創建RBAC的模型

    菜單 

      public class SysMenu
        {
            /// <summary>
            ///     父級
            /// </summary>
            public int ParentId { get; set; } = 0;
    
            /// <summary>
            ///     菜單名稱
            /// </summary>
            [StringLength(20)]
            public string Name { get; set; }
    
            /// <summary>
            ///     菜單地址
            /// </summary>
            [StringLength(20)]
            [Required]
            public string Url { get; set; }
    
            /// <summary>
            ///     層級
            /// </summary>
            [Column(TypeName = "tinyint(4)")]
            public int Level { get; set; } = 1;
    
            /// <summary>
            ///     菜單權限(list<int /> json)
            /// </summary>
            [StringLength(100)]
            public string Operates { get; set; }
    
            /// <summary>
            ///     排序
            /// </summary>
            public int Sort { get; set; }
    
            /// <summary>
            /// 菜單圖標
            /// </summary>
            public string Icon { get; set; }        
        }

     功能

      public class SysOperate
        {
            /// <summary>
            ///     按鈕名稱
            /// </summary>
            [StringLength(20)]
            [Required]
            public string Name { get; set; }
    
            /// <summary>
            ///     備註
            /// </summary>
            [StringLength(int.MaxValue)]
            public string Remark { get; set; }
    
            /// <summary>
            /// 唯一標識
            /// </summary>
            [Required]
            public int Unique { get; set; }
        }

    角色

      public class SysRole 
        {
            /// <summary>
            ///     角色名稱
            /// </summary>
            [StringLength(20)]
            [Required]
            public string Name { get; set; }
    
            /// <summary>
            ///     備註
            /// </summary>
            [StringLength(int.MaxValue)]
            public string Remark { get; set; }
        }

    用戶

        public class SysUser
        {
            /// <summary>
            ///     角色id
            /// </summary>
            public int RoleId { get; set; }
    
            /// <summary>
            ///     用戶名
            /// </summary>
            [StringLength(32)]
            [Required]
            public string UserName { get; set; }
    
            /// <summary>
            ///     密碼
            /// </summary>
            [StringLength(500)]
            [Required]
            public string Password { get; set; }
        }

     微服務中讓它成為一個授權權限服務

      在日常工作中,總會有很多系統要做,每個系統都要一套完整的權限功能,有現成的直接拿來粘貼複製,沒有現成的又要浪費很多時間去設計實現它。 如果有這樣一個服務,我們可以節省很多不必要的粘貼複製操作,節省很多時間。

      於是 ketchup.zero 這樣一個服務就誕生了。它是基於ketchu微服務框架來實現的一個權限授權服務,基本可以滿足我們日常工作的的權限需求。

      服務的前端是基於vue的模板d2admin 開發的。

    ketchup.zero的功能

    登陸

    面板

     

     用戶配置角色

     

     菜單設置擁有那些權限

     

     權限/功能/按鈕 管理

     

     角色設置權限

     

    最後安利

    如果它對你有幫助,請給一波start

    服務 ketchup.zero 源碼地址:https://github.com/simple-gr/ketchup.zero

    微服務框架 ketchup 源碼地址:https://github.com/simple-gr/ketchup 

    ketchup 交流群:592407137

     

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

    【其他文章推薦】

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

    ※超省錢租車方案

  • 前端業務代碼中的配置化

    前端業務代碼中的配置化

    業務代碼中的配置化

    工作中有許多邏輯冗雜、迭代頻繁的業務代碼,隨着迭代將越來越難以維護,一些場景適合通過配置化的方式來處理便於維護。

    一、什麼是業務代碼配置化?

    根據業務場景使用配置化的 Object|Array|Map 處理條件判斷邏輯,通常需要配置文件 CONFIG.js,若邏輯複雜需添加 getConfig 的處理函數 – tool.js

    • 本質上 if/else 邏輯是一種狀態匹配

    • 表驅動法,使用表數據,存儲對應的狀態處理

    • 可讀性好,減少了繁雜嵌套的 if-else,讀取配置,邏輯更清晰

    • 可維護性高,邏輯分支的增刪只是 CONFIG 的增刪

    二、如何在業務場景中進行代碼配置化?

    1. 簡單的狀態映射

    • 按需使用 Object|Map 配置
    單一條件
    • Object 形式:
    // CONFIG.JS
      export const STATUS = {
        STUDENT: 0,
        TEACHER: 1,
        MA_NONG: 2,
      };
      export const WORK_MAP = {
        STATUS.STUDENT: '學生',
        STATUS.TEACHER: '老師',
        STATUS.MA_NONG: '碼農',
      };
    
    // index.js
      this.setData({
        work: WORK_MAP[status],
      });
    
      axios.post(url, { status: STATUS.MA_NONG });
    
    • Map 形式:
    // CONFIG.JS
    export const WORK_MAP = new Map([
      [0, "學生"],
      [1, "老師"],
      [2, "碼農"],
    ]);
    // index.js
    this.setData({
      work: WORK_MAP.get(status),
    });
    
    多重條件
    const config = new Map([
      [
        (condition0, condition1, condition2) =>
          condition0 && condition1 && condition2,
        () => {
          console.log("map0");
        },
      ],
      [
        (condition0, condition1, condition2) =>
          condition0 || condition1 || condition2,
        () => {
          console.log("map1");
        },
      ],
    ]);
    config.forEach((action, _if) => _if(0, 1, 0) && action());
    

    2. 每個狀態有多種屬性

    • 多個屬性
    • 使用 Array 配置
    // CONFIG.JS
      export const CONFIG = [
        {
          status: STATUS.STUDENT,
          name: '學生',
          action: '談戀愛',
        },
        {
          status: STATUS.TEACHER,
          name: '老師',
          action: '教書',
        },
        {
          status: STATUS.MA_NONG,
          name: '碼農',
          action: '寫bug',
        },
      ];
    
    // index.js
      <!-- 根據狀態不同的行為 -->
      function action(status) {
        const { name, work } = CONFIG.find(i => i.status === status);
        console.log(`${name}在${action}`);
      }
    

    3. 每個狀態有多種屬性且參數定製化

    • 參數高度定製化,不同狀態需要適配接口不同的字段
    • 使用 Array 配置
    • 通過配置函數並傳參注入接口數據可滿足定製化需求
    // CONFIG.JS
      export const CONFIG = [
        {
          status: STATUS.STUDENT,
          name: '學生',
          action: () => {
            console.log('學生的工作是談戀愛');
          },
        },
        {
          status: STATUS.TEACHER,
          name: '老師',
          action: (info) => {
            alert(`老師${info.age}歲,每天${info.action}`);
          },
        },
        {
          status: STATUS.MA_NONG,
          name: '碼農',
          action: (info) => {
            toast(`碼農工作${info.workTime}年了,頭髮僅剩${info.hair}根了`);
          },
        },
      ];
    
    // index.js
      <!-- 根據接口狀態action -->
      function action(res) {
        const { action, info } = CONFIG.find(i => i.status === res.status);
        action && action(info); // 傳參定製化
      }
    

    三、實例

    大首頁瀑布流 item 樣式

    • 根據 list 接口下發的 item 的類型(type)&樣式(layout)字段取 item 中的封面、標題、標籤、頭像…,字段各不相同
    • 十幾種 item 類型,有的還有不同的 layout,item 數據下發方式不同
    • 公共組件,需要適配其他模塊的接口數據作展示

    index.xml

    • 數據驅動,減少模板中的判斷邏輯
    <view class="panel" bind:tap="goDetail">
      <!-- 封面 -->
      <image  wx:if="{{panel.cover}}" class="panel__cover" src="{{panel.cover.image}}">
          <view class="panel__tag {{panel.tagClass}}" wx:if="{{panel.tagClass}}">{{panel.tag}}</view>
      </image>
      <!-- 標題 -->
      <view class="panel__titl" wx:if="{{panel.title}}">{{panel.title}}</view>
      <!-- footer -->
      <view class="panel__footer" wx:if="{{panel.showFooter}}">
        <image class="panel__footer-icon" wx:if="{{panel.user.icon}}" src="{{panel.user.icon}}"></image>
        <text class="panel__footer-name" wx:if="{{panel.user.nickname}}">{{panel.user.nickname}}</text>
        <text class="panel__footer-comment" wx:if="{{panel.commentCount}}">{{panel.commentCount}}評論</text>
      </view>
    </view>
    

    CONFIG.js

    import { Layout, NewsType, Redirect } from 'src/constant';
    import { formatImage, formatUser } from './tool';
    
    /**
     * 配置項
     * @param {String} title 標題
     * @param {String} cover 封面
     * @param {String} tag 標籤
     * @param {Object} user 用戶信息
     * @param {Boolean} showFooter 是否显示footer
     * @param {Boolean} isAd 是否廣告
     * @param {Function} itemWrap 兼容接口數據函數,數據可能以ref_xxx下發,比如帖子:ref_post
     * ......
     */
    
    <!-- 默認配置項 -->
    export const DEFAULT = {
      title: ({ title = '' }) => title,
      cover: ({ image_list = [], article_images = [] }) =>
        formatImage(image_list[0]) || formatImage(article_images[0]),
      showFooter: true,
      user: ({ user, user_account, article_source_tx = '' }) =>
        user
          ? formatUser(user)
          : user_account
          ? formatUser(user_account)
          : {
              icon: '',
              nickname: article_source_tx,
            },
    };
    
    export const CONFIG = [
      {
        type: NewsType.NEWS,
        ...DEFAULT,
        tag: '資訊',
        tagClass: 'news',
      },
      {
        type: NewsType.VIDEO,
        ...DEFAULT,
        tag: '',
        tagClass: 'video',
        cover: ({ image_gif_list = [], image_list = [] }) => formatImage(image_gif_list[0] || image_list[0]),
        video: ({ video_url = '', tencent_vid = '', image_gif_list = [] }) => ({
          hasVideo: true,
          src: tencent_vid || video_url,
          video_url,
          tencent_vid,
          gifCover: formatImage(image_gif_list[0]),
        }),
      },
      {
        type: Redirect.EVAL_DETAIL,
        layouts: [
          {
            layout: Layout.EVALUATION,
            ...DEFAULT,
            tag: '口碑',
            tagClass: 'praise',
          },
          {
            layout: Layout.PRAISE_COMPONENT,
            ...DEFAULT,
            tag: '口碑',
            tagClass: 'praise',
            itemWrap: ({ ref_car_score = {} }) => ref_car_score,
            title: ({ chosen_topic = '' }) => chosen_topic,
            commentCount: ({ comment_count = null }) => comment_count,
            cover: ({ images = [], recommend_images = [] }) =>
              formatImage(images[0]) ||
              formatImage(getCoverFromRecommendImages(recommend_images)),
          },
          {
            layout: Layout.NORMAL,
            ...DEFAULT,
          },
        ],
      },
      ......
    ];
    

    tool.js

    import { CONFIG, DEFAULT, AD_CONFIG } from "./CONFIG";
    // 獲取瀑布流item數據
    export const getPanelData = (item) => {
      const getConfigByTypeAndLayout = () => {
        let config = CONFIG.find((i) => i.type == item.type);
        if (item.isAd) {
          config = AD_CONFIG;
        }
        if (config && config.layouts) {
          config = config.layouts.find(
            (i) => i.layout === item.layout_type || i.layout === item.display_style
          );
        }
        if (!config) {
          config = DEFAULT;
          console.log("no-config", item.type, item.layout_type, item);
        }
        return config;
      };
      const getPanelDataByConfig = (c) => {
        const panel = {};
        let _item = item;
        if (c.itemWrap) {
          _item = c.itemWrap(item);
        }
        Object.keys(c).forEach((key) => {
          if (typeof c[key] === "function") {
            panel[key] = c[key](_item);
          } else {
            panel[key] = c[key];
          }
        });
        return panel;
      };
      // 根據item的類型、樣式獲取配置
      const config = getConfigByTypeAndLayout(item);
      // 根據配置獲取瀑布流item信息
      return getPanelDataByConfig(config);
    };
    

    四、結語

    所以,業務代碼配置化很簡單,大家也都一直在用,只是如果在一些業務場景中都形成配置化的習慣或者共識,可能更好維護吧。

    鄙人拙見,大佬賜教~

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

    【其他文章推薦】

    新北清潔公司,居家、辦公、裝潢細清專業服務

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

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

    ※超省錢租車方案

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

  • 基於層級表達的高效網絡搜索方法 | ICLR 2018

    基於層級表達的高效網絡搜索方法 | ICLR 2018

    論文基於層級表達提出高效的進化算法來進行神經網絡結構搜索,通過層層堆疊來構建強大的卷積結構。論文的搜索方法簡單,從實驗結果看來,達到很不錯的準確率,值得學習

    來源:【曉飛的算法工程筆記】 公眾號

    論文: Hierarchical Representations for Efficient Architecture Search

    • 論文地址:https://arxiv.org/abs/1711.00436

    Introduction

      由於網絡的驗證需要耗費很長的時間,神經網絡結構搜索計算量非常巨大,很多研究通過降低搜索空間的複雜度來提高搜索的效率。論文通過加入分層網絡結構來約束搜索空間,在最初幾層僅使用卷積和池化等簡單操作,逐步到高層將底層的block進行組合搭建,最後將最高層的block堆疊成最終的網絡。由於搜索空間設計夠好,網絡的搜索方法僅用進化算法或隨機搜索足以。
      論文總結如下:

    • 提出對神經網絡結構的層級表達
    • 通過實驗證明搜索空間的設計十分重要,可以降低搜索方法的投入,甚至隨機搜索也可以
    • 提出可擴展的進化搜索方法,對比其它進化搜索方法有更好的結果

    Architecture Representations

    Flat Architecture Representation

      將神經網絡結構定義為單輸入、單輸出的計算圖,圖中每個節點代表特徵圖,每條有向邊為基本操作(卷積、池化等),所以網絡的表達$(G,o)$包含兩部分:

    1. 一個有效的操作集合$o={o_1,o_2,…}$
    2. 一個鄰接矩陣$G$,用以指定操作的神經網絡圖,$G_{ij}=k$為節點$i$和節點$j$間的操作為$o_k$

      將操作集$o$和鄰接矩陣$G$組合起來就得到網絡的結構

      每個節點$i$的特徵圖$x_i$由其前面的節點$j$通過公式2計算而得,$|G|$是圖中節點數量,$merge$將多個特徵圖合併成一個的操作,這裏直接使用depthwise concatentation,由於element-wise addition要求維度一致,比較不靈活,而且如果融合特徵後接的是$1\times 1$卷積,這就其實類似於做concatienation

    Hierarchical Architecture Representation

      層級結構表達的關鍵是找到不同的層級的模版,在構建高層模版時使用低層的模版作為積木(operation)進行構建

      對於$L$層的層級關係,$\ell$層包含$M_{\ell}$個模版,最高層$\ell=L$僅包含一個模版,對應完整的網絡,最低層$\ell=1$是元操作集,定義$o_m{(\ell)}$為$\ell$層的第$m$個模版,為低層模版$o{(\ell)}={o_1^{(\ell -1)},o_2^{(\ell -1)},…,o_1^{(\ell – 1)}}$根據公式3的組合。最終的層級結構表達為$({{G_m{(\ell)}}_{m=1}M}_{\ell=2}L,o{(1)})$,由每層的模版的網絡結構關係和最底層操作定義,如圖1

    Primitive Operations

      低層的原操作共六種($\ell=1$,$M_t=6$):

    • 1 × 1 convolution of C channels
    • 3 × 3 depthwise convolution
    • 3 × 3 separable convolution of C channels
    • 3 × 3 max-pooling
    • 3 × 3 average-pooling
    • identity

      使用時,所有元操作為stride=1,以及進行padded來保留分辨率,卷積后都接BN+ReLU,維度固定為$C$。另外每層都有$none$操作,代表節點$i$和節點$j$之間沒有連接

    Evolutionary Architecture Search

    Mutation

      分層基因的變異包含以下步驟:

    • 採樣一個非原始層$\ell\ge2$作為目標層
    • 在目標層採樣一個模版$m$作為目標模版
    • 在目標模版中採樣一個後繼節點$i$
    • 在目標模版中採樣一個前置節點$j$
    • 隨機替換當前操作$o_k^{(\ell -1)}$為其它操作$o_{k{‘}}{(\ell -1)}$

      對於當前層級只有兩層的,第一步直接將$\ell$設為2,變異可總結為公式4,$\ell$,$m$,$i$,$j$,$k^{‘}$從各自區域的均勻分佈中隨機抽樣得到,上面的突變足夠對模版產生3種修改:

    • 添加邊:$o_k^{(\ell -1)}=none$,$o_{k{‘}}{(\ell -1)}\ne none$
    • 修改存在的邊:$o_k^{(\ell -1)}\ne none$,$o_{k{‘}}{(\ell -1)}\ne none$,$o_k^{(\ell -1)}\ne o_{k{‘}}{(\ell -1)}$
    • 刪除存在的邊:$o_k^{(\ell -1)}\ne none$,$o_{k{‘}}{(\ell -1)}= none$

    Initialization

      基因指代完整的網絡,基因的種群初始化包含兩個步驟:

    1. 建立一個不重要的基因,每個模版都使用identity進行連接
    2. 對基因進行大批量的隨機變異來多樣化

      對比以前的研究使用常見的網絡進行基因初始化,這樣的初始化不僅能很好地覆蓋不常見的網絡的搜索空間,還能去除人工初始化帶來的傳統偏向

    Search Algorithms

      論文的進化算法基於錦標賽選擇(tournament selection),首先對初始化的種群網絡進行訓練和測試得到分數,然後從種群中隨機獲取5%的基因,表現最好的基因進行突變得到新網絡,在訓練和測試後放入種群中,重複進行上述選取與放回,種群數量不斷增大,最終取種群表現最好的基因
      論文也使用隨機搜索進行實驗,基因種群隨機生成,然後進行訓練和驗證,選取最好的模型,這種方法的主要好處在於能整個種群并行化計算,減少搜索時間

    Implementation

      論文使用異步分佈式進行實現,包含一個controller和多個worker,分別負責基因的進化和測試,兩者共享一個內存表格$\mathcal{M}$,記錄基因及其準確率(fitness),還有一個數據隊列$\mathcal{Q}$,包含待測試的基因

      當有worker空餘時,controller使用錦標賽選擇從$\mathcal{M}$中選擇一個基因進行突變,然後放到隊列$\mathcal{Q}$中等待測試

      worker從$\mathcal{Q}$中拿到待測試的基因,測試後放到$\mathcal{M}$中,訓練是從頭開始訓練的,沒有使用權值共享加速

    Experiments and Results

    Experimental Setup

      在實驗中,沒有對整體網絡進行搜索,而是使用提出的方法進行卷積單元(cell)的搜索,這樣能夠在小網絡上快速進行網絡測試然後遷移到較大的網絡。具體的各結構如圖2,每個cell後面接$2c$維度和$stride=2$的$3\times 3$分離卷積,用於升維和降低分辨率,最後一個cell後面接$c$維度和$stride=1$的$3\times 3$分離卷積

    Architecture Search on CIFAR-10

      200卡,初始種群為200,層級$L=3$,每層模版的操作分別為$M_1=6$,$M_2=6$和$M_3=1$,每層($\ell \ge2$)的節點圖分別為$|G{(2)}|=4$和$|G{(3)}|=5$,層2的模版跟一個跟模版輸入維度一樣$1\times 1$的卷積來降維。對於用於對比的不分層的搜索方法,則使用11個節點的計算圖。從圖3來看,論文提出的方法在收斂速度、準確率和參數量上都不錯

      為了進一步展示論文方法的效果,對圖3中間的結果的每輪增量進行了可視化。在P100 GPU上,每個網絡的測試需要花費1小時,進化共7000輪,200張卡共需要1.5天

    Architecture Evaluation on CIFAR-10 and ImageNet

    CONCLUSION

      論文基於層級表達提出高效的進化算法來進行神經網絡結構搜索,通過層層堆疊來構建強大的卷積結構。論文的搜索方法簡單,從實驗結果看來,200張卡共需要1.5天,達到很不錯的準確率,值得學習

    APPENDIX A



    如果本文對你有幫助,麻煩點個贊或在看唄~
    更多內容請關注 微信公眾號【曉飛的算法工程筆記】

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

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

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

    ※台北網頁設計公司全省服務真心推薦

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

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心