標籤: 南投搬家公司費用

  • 為何有點小錢的人都不願買國產車?

    為何有點小錢的人都不願買國產車?

    廢話這麼多,歸根結底說白了就是一個字 – 錢。當你的財力和社會地位提升起來后,相信你的內心自然會有答案。

    2016年汽車圈發生了很多事,其中令叫獸印象最深的是自主品牌的叫好聲四起,銷量上也取得了空前的突破。不過當叫獸冷靜下來理智的想一想,當下自主品牌真的是形勢一片大好,即將崛起了嗎?

    廢話這麼多,歸根結底說白了就是一個字 – 錢。當你的財力和社會地位提升起來后,相信你的內心自然會有答案。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

  • 機器學習——手把手教你用Python實現回歸樹模型

    機器學習——手把手教你用Python實現回歸樹模型

    本文始發於個人公眾號:TechFlow,原創不易,求個關注

    今天這篇是機器學習專題的第24篇文章,我們來聊聊回歸樹模型。

    所謂的回歸樹模型其實就是用樹形模型來解決回歸問題,樹模型當中最經典的自然還是決策樹模型,它也是幾乎所有樹模型的基礎。雖然基本結構都是使用決策樹,但是根據預測方法的不同也可以分為兩種。第一種,樹上的恭弘=叶 恭弘子節點就對應一個預測值和分類樹對應,這一種方法稱為回歸樹。第二種,樹上的恭弘=叶 恭弘子節點對應一個線性模型,最後的結果由線性模型給出。這一種方法稱為模型樹。

    今天我們先來看看其中的回歸樹。

    回歸樹模型

    回歸樹模型的核心算法,也就是構建決策樹的算法,就是我們上篇文章所講的CART算法。如果有生疏或者是遺漏的同學,可以通過下方傳送門回顧一下:

    機器學習——十大數據挖掘之一的決策樹CART算法

    CART算法的核心精髓就是我們每次選擇特徵對數據進行拆分的時候,永遠對數據集進行二分。無論是離散特徵還是連續性特徵,一視同仁。CART還有一個特點是使用GINI指數而不是信息增益或者是信息增益比來選擇拆分的特徵,但是在回歸問題當中用不到這個。因為回歸問題的損失函數是均方差,而不是交叉熵,很難用熵來衡量連續值的準確度。

    在分類樹當中,我們一個恭弘=叶 恭弘子節點代表一個類別的預測值,這個類別的值是落到這個恭弘=叶 恭弘子節點當中訓練樣本的類別的眾數,也就是出現頻率最高的類別。在回歸樹當中,恭弘=叶 恭弘子節點對應的自然就是一個連續值。這個連續值是落到這個節點的訓練樣本的均值,它的誤差就是這些樣本的均方差。

    另外,之前我們在選擇特徵的劃分閾值的時候,對閾值的選擇進行了優化,只選擇了那些會引起預測類別變化的閾值。但是在回歸問題當中,由於預測值是一個浮點數,所以這個優化也不存在了。整體上來說,其實回歸樹的實現難度比分類樹是更低的。

    實戰

    我們首先來加載數據,我們這次使用的是scikit-learn庫當中經典的波士頓房價預測的數據。關於房價預測,kaggle當中也有一個類似的比賽,叫做:house-prices-advanced-regression-techniques。不過給出的特徵更多,並且存在缺失等情況,需要我們進行大量的特徵工程。感興趣的同學可以自行研究一下。

    首先,我們來獲取數據,由於sklearn庫當中已經有數據了,我們可以直接調用api獲取,非常簡單:

    import numpy as np
    import pandas as pd
    from sklearn.datasets import load_boston
    boston = load_boston()
    
    X, y = boston.data, boston.target
    

    我們輸出前幾條數據查看一下:

    這個數據質量很高,sklearn庫已經替我們做完了數據篩選與特徵工程,直接拿來用就可以了。為了方便我們傳遞數據,我們將X和y合併在一起。由於y是一維的數組形式是不能和二維的X合併的,所以我們需要先對y進行reshape之後再進行合併。

    y = y.reshape(-1, 1)
    X = np.hstack((X, y))
    

    hstack函數可以將兩個np的array橫向拼接,與之對應的是vstack,是將兩個array縱向拼接,這個也是常規操作。合併之後,y作為新的一列添加在了X的後面。數據搞定了,接下來就要輪到實現模型了。

    在實現決策樹的主體部分之前,我們先來實現兩個輔助函數。第一個輔助函數是計算一批樣本的方差和,第二個輔助函數是獲取樣本的均值,也就是子節點的預測值。

    def node_mean(X):
        return np.mean(X[:, -1])
    
    
    def node_variance(X):
        return np.var(X[:, -1]) * X.shape[0]
    

    這個搞定了之後,我們繼續實現根據閾值拆分數據的函數。這個也可以復用之前的代碼:

    from collections import defaultdict
    def split_dataset(X, idx, thred):
        split_data = defaultdict(list)
        for x in X:
            split_data[x[idx] < thred].append(x)
        return list(split_data.values()), list(split_data.keys())
    

    接下來是兩個很重要的函數,分別是get_thresholds和split_variance。顧名思義,第一個函數用來獲取閾值,前面說了由於我們做的是回歸模型,所以理論上來說特徵的每一個取值都可以作為切分的依據。但是也不排除可能會存在多條數據的特徵值相同的情況,所以我們對它進行去重。第二個函數是根據閾值對數據進行拆分,返回拆分之後的方差和。

    def get_thresholds(X, i):
        return set(X[:, i].tolist())
    
    # 每次迭代方差優化的底線
    MINIMUM_IMPROVE = 2.0
    # 每個恭弘=叶 恭弘子節點最少樣本數
    MINIMUM_SAMPLES = 10
    
    def split_variance(dataset, idx, threshold):
        left, right = [], []
        n = dataset.shape[0]
        for data in dataset:
            if data[idx] < threshold:
                left.append(data)
            else:
                right.append(data)
        left, right = np.array(left), np.array(right)
        # 預剪枝
        # 如果拆分結果有一邊過少,則返回None,防止過擬合
        if len(left) < MINIMUM_SAMPLES or len(right) < MINIMUM_SAMPLES:
            return None
        # 拆分之後的方差和等於左子樹的方差和加上右子樹的方差和
        # 因為是方差和而不是均方差,所以可以累加
        return node_variance(left) + node_variance(right)
    

    這裏我們用到了MINIMUM_SAMPLES這個參數,它是用來預剪枝用的。由於我們是回歸模型,如果不對決策樹的生長加以限制,那麼很有可能得到的決策樹的恭弘=叶 恭弘子節點和訓練樣本的數量一樣多。這顯然就陷入了過擬合了,對於模型的效果是有害無益的。所以我們要限制每個節點的樣本數量,這個是一個參數,我們可以根據需要自行調整。

    接下來,就是特徵和閾值篩選的函數了。我們需要開發一個函數來遍歷所有可以拆分的特徵和閾值,對數據進行拆分,從所有特徵當中找到最佳的拆分可能。

    def choose_feature_to_split(dataset):
        n = len(dataset[0])-1
        m = len(dataset)
        # 記錄最佳方差,特徵和閾值
        var_ = node_variance(dataset)
        bestVar = float('inf')
        feature = -1
        thred = None
        for i in range(n):
            threds = get_thresholds(dataset, i)
            for t in threds:
                # 遍歷所有的閾值,計算每個閾值的variance
                v = split_variance(dataset, i, t)
                # 如果v等於None,說明拆分過擬合了,跳過
                if v is None:
                    continue
                if v  < bestVar:
                    bestVar, feature, thred = v, i, t
        # 如果最好的拆分效果達不到要求,那麼就不拆分,控制子樹的數量
        if var_ - bestVar < MINIMUM_IMPROVE:
            return None, None
        return feature, thred
    
    

    和上面一樣,這個函數當中也用到了一個預剪枝的參數MINIMUM_IMPROVE,它衡量的是每一次生成子樹帶來的收益。當某一次生成子樹帶來的收益小於某個值的時候,說明收益很小,並不划算,所以我們就放棄這次子樹的生成。這也是預剪枝的一種。

    這些都搞定了之後,就可以來建樹了。建樹的過程和之前類似,只是我們這一次的數據當中沒有特徵的name,所以我們去掉特徵名稱的相關邏輯。

    def create_decision_tree(dataset):
        dataset = np.array(dataset)
        
        # 如果當前數量小於10,那麼就不再繼續劃分了
        if dataset.shape[0] < MINIMUM_SAMPLES:
            return node_mean(dataset)
        
        # 記錄最佳拆分的特徵和閾值
        fidx, th = choose_feature_to_split(dataset)
        if fidx is None:
            return th
        
        node = {}
        node['feature'] = fidx
        node['threshold'] = th
        
        # 遞歸建樹
        split_data, vals = split_dataset(dataset, fidx, th)
        for data, val in zip(split_data, vals):
            node[val] = create_decision_tree(data)
        return node
    

    我們來完整測試一下建樹,首先我們需要對原始數據進行拆分。將原始數據拆分成訓練數據和測試數據,由於我們的場景比較簡單,就不設置驗證數據了。

    拆分數據不用我們自己實現,sklearn當中提供了相應的工具,我們直接調用即可:

    from sklearn.model_selection import train_test_split
    
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=23)
    

    我們一般用到的參數就兩個,一個是test_size,它可以是一個整數也可以是一個浮點數。如果是整數,代表的是測試集的樣本數量。如果是一個0-1.0的浮點數,則代表測試集的佔比。random_state是生成隨機數的時候用到的隨機種子。

    我們輸出一下生成的樹,由於數據量比較大,可以看到一顆龐大的樹結構。建樹的部分實現了之後,最後剩下的就是預測的部分了。

    預測部分的代碼和之前分類樹相差不大,整體的邏輯完全一樣,只是去掉了feature_names的相關邏輯。

    def classify(node, data):
        key = node['feature']
        pred = None
        thred = node['threshold']
    
        if isinstance(node[data[key] < thred], dict):
            pred = classify(node[data[key] < thred], data)
        else:
            pred = node[data[key] < thred]
                
        # 放置pred為空,挑選一個恭弘=叶 恭弘子節點作為替補
        if pred is None:
            for key in node:
                if not isinstance(node[key], dict):
                    pred = node[key]
                    break
        return pred
    

    由於這個函數一次只能接受一條數據,如果我們想要批量預測的話還不行,所以最好的話再實現一個批量預測的predict函數比較好。

    def predict(node, X):
        y_pred = []
        for x in X:
            y = classify(node, x)
            y_pred.append(y)
        return np.array(y_pred)
    

    后剪枝

    后剪枝的英文原文是post-prune,但是翻譯成事後剪枝也有點奇怪。anyway,我們就用后剪枝這個詞好了。

    在回歸樹當中,我們利用的思想非常樸素,在建樹的時候建立一棵盡量複雜龐大的樹。然後在通過測試集對這棵樹進行修剪,修剪的邏輯也非常簡單,我們判斷一棵子樹存在分叉和沒有分叉單獨成為恭弘=叶 恭弘子節點時的誤差,如果修剪之後誤差更小,那麼我們就減去這棵子樹。

    整個剪枝的過程和建樹的過程一樣,從上到下,遞歸執行。

    整個邏輯很好理解,我們直接來看代碼:

    def is_dict(node):
        return isinstance(node, dict)
    
    
    def prune(node, testData):
        testData = np.array(testData)
        if testData.shape[0] == 0:
            return node
     
        # 拆分數據
        split_data, _ = split_dataset(testData, node['feature'], node['threshold'])
        # 對左右子樹遞歸修剪
        if is_dict(node[0]):
            node[0] = prune(node[0], split_data[0])
        if is_dict(node[1]) and len(split_data) > 1:
            node[1] = prune(node[1], split_data[1])
    
        # 如果左右都是恭弘=叶 恭弘子節點,那麼判斷當前子樹是否需要修剪
        if len(split_data) > 1 and not is_dict(node[0]) and not is_dict(node[1]):
            # 計算修剪前的方差和
            baseError = np.sum(np.power(np.array(split_data[0])[:, -1] - node[0], 2)) + np.sum(np.power(np.array(split_data[1])[:, -1] - node[1], 2))
            # 計算修剪后的方差和
            meanVal = (node[0] + node[1]) / 2
            mergeError = np.sum(np.power(meanVal - testData[:, -1], 2))
            if mergeError < baseError:
                return meanVal
            else:
                return node
        return node
    

    最後,我們對修剪之後的效果做一下驗證:

    從圖中可以看到,修剪之前我們在測試數據上的均方差是19.65,而修剪之後降低到了19.48。從數值上來看是有效果的,只是由於我們的訓練數據比較少,同時進行了預剪枝,影響了后剪枝的效果。但是對於實際的機器學習工程來說,一個方法只要是有明確效果的,在代價可以承受的範圍內,它就是有價值的,千萬不能覺得提升不明顯,而隨便否定一個方法。

    這裏計算均方差的時候用到了sklearn當中的一個庫函數mean_square_error,從名字當中我們也可以看得出來它的用途,它可以對兩個Numpy的array計算均方差

    總結

    關於回歸樹模型的相關內容到這裏就結束了,我們不僅親手實現了模型,而且還在真實的數據集上做了實驗。如果你是親手實現的模型的代碼,相信你一定會有很多收穫。

    雖然從實際運用來說我們幾乎不會使用樹模型來做回歸任務,但是回歸樹模型本身是非常有意義的。因為在它的基礎上我們發展出了很多效果更好的模型,比如大名鼎鼎的GBDT。因此理解回歸樹對於我們後續進階的學習是非常重要的。在深度學習普及之前,其實大多數高效果的模型都是以樹模型為基礎的,比如隨機森林、GBDT、Adaboost等等。可以說樹模型撐起了機器學習的半個時代,這麼說相信大家應該都能理解它的重要性了吧。

    今天的文章就到這裏,如果喜歡本文,可以的話,請點個關注,給我一點鼓勵,也方便獲取更多文章。

    本文使用 mdnice 排版

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

    【其他文章推薦】

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

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

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

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

    ※回頭車貨運收費標準

  • Android官方新推的DI庫 Hilt

    Android官方新推的DI庫 Hilt

    Hilt是Google Android官方新推薦的依賴注入工具.
    已加入到官方文檔: Dependency injection with Hilt. 目前是alpha release階段.

    Hilt是在Dagger之上, Hilt單詞的意思是: 刀把, 柄.
    代碼庫還是這個google/dagger.

    Hilt的出現, 讓我想起了曾經曇花一現的dagger.android, 不知道hilt能不能經得住時間的考驗.

    本文介紹Hilt的基本使用. 熟悉dagger的朋友可能會發現很容易.

    Hilt是花里胡哨的小打小鬧? 還是下一個主流工具? 讓我們拭目以待.

    Codelab練習例子

    • Codelab: Using Hilt in your Android app.

    我的Fork: https://github.com/mengdd/android-hilt.

    這個項目最開始是一個用ServiceLocator手動注入的例子, 功能是記錄用戶點擊button操作, 显示log. 有兩個Fragment和Activity.

    通過Codelab中一系列步驟的改造, 最終改成用Hilt做依賴注入.

    本文舉例就用其中的代碼片段了.

    原repo還貼心地附上了改造前後的對比diff: Full codelab comparison.

    Hilt依賴添加

    project root: build.gradle:

    buildscript {
        ext.hilt_version = '2.28-alpha'
        ...
        dependencies {
            ...
            classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
        }
    }
    

    需要Android 4.0以上.

    app/build.gradle:

    apply plugin: 'kotlin-kapt'
    apply plugin: 'dagger.hilt.android.plugin'
    
    android {
        ...
    }
    
    dependencies {
        // Hilt dependencies
        implementation "com.google.dagger:hilt-android:$hilt_version"
        kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
    
        // Hilt testing dependency
        androidTestImplementation "com.google.dagger:hilt-android-testing:$hilt_version"
        // Make Hilt generate code in the androidTest folder
        kaptAndroidTest "com.google.dagger:hilt-android-compiler:$hilt_version"
    }
    

    後面兩個是測試的依賴.

    概念, 用詞解釋

    • Container, 對應Component.
    • Bindings, 對應依賴.
    The information Hilt has about how to provide instances of different types are also called bindings.
    
    • Component hierarchy是指依賴在當前component中可以用, 也可以在它包含的child component中用. 舉例: application容器中的依賴activity可以用, 但是反過來不行.

    Hilt基本使用

    • @HiltAndroidApp: 標記在Application類上, 觸發代碼生成, application container是整個app的parent container.
    @HiltAndroidApp
    class LogApplication : Application()
    
    • @AndroidEntryPoint: 標記在Activity和Fragment上. 創建了一個和當前Activity/fragment生命周期相關的container. 目前支持的類型是: Activity, Fragment, View, Service, BroadcastReceiver.
    @AndroidEntryPoint
    class ExampleActivity : AppCompatActivity() { ... }
    

    每次Fragment(或Activity等)創建都會有它對應的container實例.

    • 字段注入: 用@Inject標記字段, 注意字段不能是private的.
    @AndroidEntryPoint
    class LogsFragment : Fragment() {
    
        @Inject
        lateinit var dateFormatter: DateFormatter
        ...
    }
    


    Component lifetimes可以看到每種類型的component創建和注入的時間.

    Activity是onCreate(), Fragment是onAttach().

    對Fragment來說, 在onAttach()的時候完成了對象的注入, 之後訪問對象都沒有問題.

    • 提供依賴: 用@Inject標記構造器.
    class DateFormatter @Inject constructor() {
        ...
    }
    

    它的依賴可以在構造參數中標明.

    Hilt的Scope支持

    默認所有的依賴都是沒有scope的, 每次注入依賴都創建新的實例.
    Hilt自動在對應的生命周期創建/銷毀對象: Component lifetimes.

    也可以把依賴scope到某個component, 這樣在這個component內, 依賴就是單例.

    • scope: @Singleton: application container的scope, 說明是application範圍內的單例. @ActivityScoped對應activity component.

    所有可用的scope: Component scopes.

    module

    module的使用基本和dagger一樣, 用來提供一些無法用構造@Inject的依賴, 比如接口, 第三方庫類型, Builder模式構造的對象等.

    • @Module: 標記這是一個module. 在Kotlin代碼中, module可以是一個object.
    • @Provides: 標記方法, 提供返回值類型的依賴.
    • @Binds: 標記抽象方法, 返回接口類型, 實現是方法的唯一參數.

    Hilt多了一個註解:

    • @InstallIn: 指明module對應哪個container, 也即Component.
      Generated components for Android classes
    @InstallIn(ApplicationComponent::class)
    @Module
    object DatabaseModule {
    
        @Provides
        @Singleton
        fun provideDatabase(@ApplicationContext appContext: Context): AppDatabase {
            return Room.databaseBuilder(
                appContext,
                AppDatabase::class.java,
                "logging.db"
            ).build()
        }
    
        @Provides
        fun provideLogDao(database: AppDatabase): LogDao {
            return database.logDao()
        }
    }
    

    module的拆分

    module的名字最好能傳達它提供了什麼類型的依賴, 所以多種依賴拆分多個modules寫比較好.

    @InstallIn(ActivityComponent::class)
    @Module
    abstract class NavigationModule {
        @Binds
        abstract fun bindNavigator(impl: AppNavigatorImpl): AppNavigator
    }
    

    @Binds@Provides不能放在同一個module里.
    因為@Binds需要module是一個abstract class, 而@Provides需要module是一個object.
    放一起會報錯: error: A @Module may not contain both non-static and abstract binding methods.

    默認依賴

    有一些默認依賴是已經有的, 比如:

    • @ApplicationContext.
    • @ActivityContext.

    可以直接作為@Provides方法或@Inject構造的參數.

    默認依賴是和component對應的.

    Component default bindings

    Qualifier

    要提供同一個接口的不同實現, 可以用不同的註解來標記. (dagger之前用的是@Named).

    A qualifier is an annotation used to identify a binding.

    舉例: LoggerDataSource接口提供了內存和數據庫兩種實現.

    定義兩個註解:

    @Qualifier
    annotation class InMemoryLogger
    
    @Qualifier
    annotation class DatabaseLogger
    

    module中提供的時候用來標記相應的依賴:

    @InstallIn(ApplicationComponent::class)
    @Module
    abstract class LoggingDatabaseModule {
    
        @DatabaseLogger
        @Singleton
        @Binds
        abstract fun bindDatabaseLogger(impl: LoggerLocalDataSource): LoggerDataSource
    }
    
    @InstallIn(ActivityComponent::class)
    @Module
    abstract class LoggingInMemoryModule {
    
        @InMemoryLogger
        @ActivityScoped
        @Binds
        abstract fun bindInMemoryLogger(impl: LoggerInMemoryDataSource): LoggerDataSource
    }
    

    這裏用了兩個module因為它們對應兩個不同的component, 一個是application一個是activity, 依賴也是相應的scope.

    注入的時候也對應加上:

    @InMemoryLogger
    @Inject
    lateinit var logger: LoggerDataSource
    

    @EntryPoint

    Hilt支持最常用的Android組件, 對於默認不支持的類型, 如果還想做字段注入, 需要用@EntryPoint.

    注意這裏只是限制了字段注入的情況, 自定義類型一般用構造注入比較方便(如果能用的話).

    @EntryPoint的意思就是一個邊界點, 這裏可以通往Hilt的世界, 得到容器提供的依賴對象們.

    Codelab中的例子是一個ContentProvider.

    關鍵的部分是這段代碼:

    @InstallIn(ApplicationComponent::class)
    @EntryPoint
    interface LogsContentProviderEntryPoint {
        fun logDao(): LogDao
    }
    
    private fun getLogDao(appContext: Context): LogDao {
        val hiltEntryPoint = EntryPointAccessors.fromApplication(
            appContext,
            LogsContentProviderEntryPoint::class.java
        )
        return hiltEntryPoint.logDao()
    }
    

    Hilt and Dagger

    當初的dagger.android已被放棄, 它是為了簡化dagger在Android上的使用而單獨推出的. (根據Activity和Fragment的生命周期, 開發者不用手動調用inject方法, 但是確實最開始的setup code比較多.)

    Hilt相對於Dagger來說, 簡化了幾個點:

    • 不用自己創建Component.
    • 不用手動調用inject()方法來完成字段注入.
    • 不用自己在Application中保存component.
    • 提供了一些Scope, 不用自己定義和寫.
    • 提供了一些默認依賴, 比如Context.

    總體來說Hilt就是針對Android做的定製, 讓依賴注入框架用起來更方便. 畢竟dagger是一個java注入庫, 它的應用範圍不限於Android.

    因為Hilt和Dagger可以共存, 可以逐步遷移. 既然官方推薦了, 可以在項目內小範圍地先試試.

    最後推薦這個cheat sheet

    Reference

    • Dependency Injection on Android with Hilt
    • Dependency injection with Hilt
    • Dagger Hilt

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

    【其他文章推薦】

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

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

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

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

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

  • 豪雨引發土石流洪災 印度尼泊爾過去1週至少41死

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

    尼泊爾和印度官員今(31日)表示,過去一週豪雨導致洪水及土石流,兩國共有至少41人喪命。南亞年度雨季進入最後階段,在各國已奪走數百人命。

    尼泊爾內政部官員表示,西部偏遠地區昨天暴雨導致土石流,埋沒5戶住家,並造成10人死亡,死者包括4名孩童。路透社報導,多山的尼泊爾今年至少已269人死於土石流與洪水,另有76人失蹤。

    印度國家緊急應變中心也表示,西部古茶拉底省(Gujarat)過去2天內已有14人因與大雨和洪水相關事故喪命。在東部的奧里薩省(Odisha),過去1週洪水也奪走至少17條人命,造成數以千計民眾流離失所,影響逾50萬人。

    氣候變遷
    國際新聞
    印度
    尼泊爾
    豪雨
    土石流
    洪災

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

    【其他文章推薦】

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

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

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

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

    ※回頭車貨運收費標準

  • 全球最大濕地「內外燃燒」中 潘塔納爾火勢15年來最嚴重

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

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

    【其他文章推薦】

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

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

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

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

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

  • 燃燒的西伯利亞:北極野火碳排量創紀錄 已較2019全年高出35%

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

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

    【其他文章推薦】

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

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

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

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

    ※回頭車貨運收費標準

  • 美上千民眾收到來路不明包裹 亞馬遜禁外國賣家銷售種子

    摘錄自2020年9月6日中央社報導

    數以千計的美國民眾收到未曾訂購、郵戳大多顯示來自中國的種子包裹,引發關注後,電子商務龍頭亞馬遜公司(Amazon)表示,已經禁止外國賣家向美國銷售種子。

    亞馬遜今(6日)在電郵聲明中表示:「未來我們只允許美國賣家銷售種子。」亞馬遜是從本月3日變更對植物種子的銷售政策。如果賣家不遵守亞馬遜的規定,可能會有帳號移除、商品下架等處分。根據亞馬遜的政策網頁,禁止銷售範圍也擴及植株及植物產品。

    美國農業部(USDA)於7月表示,這些不明的包裹有可能是電子商務欺騙手法的一環,賣方透過寄發未經訂購的產品來捏造不實的正面評論,提升自己業績。

    農業部動植物衛生檢驗局(APHIS)官員8月11日在最新報告中說,專家分析部分來自中國的種子後,發現問題並不多,且目前兩國正聯手調查中。

    生物多樣性
    國際新聞
    美國
    種子

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

    【其他文章推薦】

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

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

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

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

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

  • 小編辣評6-8萬熱門SUV!說完會不會被炒魷魚?

    小編辣評6-8萬熱門SUV!說完會不會被炒魷魚?

    前置后驅載貨也是一流的,但1。6L動力剛好夠用而已。想動力強加錢上1。5T吧”東風風光580指導價:7。29-9。99萬“又一款7座中型SUV,價格也與長安CX70相似,硬抗的節奏。不過衝著配置以及顏值,我選風光580”東風風神AX3指導價:6。

    在留言中,小編總是會遇到很多很多的留言,都是詢問某某車型是怎樣的。那麼今天小編就來一個超大的集合,簡單但客觀地評論6-8萬區間的SUV熱門車型。這樣的評論小編真擔心自己的飯碗吶~

    寶駿560

    指導價:6.98-10.58萬

    “寶駿神車物美價廉,空間大油耗低配置高,但就是底盤表現沒有那麼的SUV。”

    北汽幻速S2

    指導價:5.18-6.08萬

    “價格非常低的SUV車型滿足你的SUV夢,空間還是可以的,但底盤以及操控還是別想着太SUV”

    北汽幻速S3

    指導價:5.38-6.68萬

    “就問你,五萬多的7座緊湊型SUV還想着怎樣?不過小編覺得要是出五座版本的話,空間表現絕對是逆天的”

    紳寶X35

    指導價:6.58-8.88萬

    “總體中規中矩沒有太多亮點,但是來自北汽紳寶讓它可靠性更高,內飾也挺對年輕人口味”

    比速T3

    指導價:7.49-8.69萬

    “配置水平極高,使用的還是1.3T渦輪增壓發動機,就是比速的4S店在哪裡?”

    比亞迪元

    指導價:7.49-8.69萬

    “國產中最愛堆配置的品牌,顏值頗高,背着小書包非常個性,但就看合不合你胃口”

    長安CX70

    指導價:6.89-10.99萬

    “不到7萬買到中型7座SUV,還能有誰?前置后驅載貨也是一流的,但1.6L動力剛好夠用而已。想動力強加錢上1.5T吧”

    東風風光580

    指導價:7.29-9.99萬

    “又一款7座中型SUV,價格也與長安CX70相似,硬抗的節奏?不過衝著配置以及顏值,我選風光580”

    東風風神AX3

    指導價:6.97-8.77萬

    “啊?原來這貨是SUV,我還以為是旅行車呢?顏值不錯,但性價比比較一般”

    東南DX3

    指導價:6.79-10.09萬

    “要顏值有顏值,要操控有操控,年輕人的最愛,後輪還是多連桿式獨立懸架。”

    哈弗H1

    指導價:5.49-7.89萬

    “別以為長城M2換了一個馬甲我就不認得你,ESp配置率較高值得推薦,但其餘都是中規中矩”。

    大邁X5

    指導價:6.99-12.19萬

    “你覺得它外觀像誰呢?配置外觀表現優秀,但就是做工方面以及質感方面需要加強”

    小編總結:

    對這個價位的車型而言,成本的原因它們是無法做到盡善盡美的,要配置還是要動力?兩者都要做到的話,駕駛品質以及做工精細水平能保證嗎?所以小編覺得的是選擇滿足自己最大需求的車型就好了。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

    ※回頭車貨運收費標準

  • 15萬買車要最個性最實用 恐怕只能選擇這三輛!

    15萬買車要最個性最實用 恐怕只能選擇這三輛!

    東風日產-啟辰T90指導價:10。98-15。48萬說到個性,如今關注度極高的啟辰T90是不能落下的,它定位為中型SUV,採用了與寶馬、奔馳、謳歌等豪華品牌類似的轎跑式SUV設計風格。2765mm的軸距、4793mm的車身長度讓它的側面造型修長而且非常動感。

    現在的車型除了配置高、動力強以外也越來越講究功能性。關於功能性好與不好,其實可以簡單理解為這些車型能不能“裝”、能不能適應各種道路狀況!

    “裝”當然是指車輛的裝載能力,實際體現如三廂轎車很難裝載一個冰箱或洗衣機!而大兩廂車型或SUV就能輕鬆裝載。

    (還記得之前爆火的“後置發動雞、雙渦輪增鴨”嗎?)

    能不能適應各種道路狀況就體現在車輛的離地間隙是否夠高,它能防止托底的情況出現,還要有濾振到位的懸架,走爛路時車內的乘客保持舒適的體驗!

    當然,現在買車還要求外觀漂亮,最好要足夠個性!還有更重要的是“逼格”!

    今天我們就來看看15萬的預算該買哪些兼顧個性、實用性的車型。

    一汽-大眾-蔚領

    指導價:12.59-16.29萬

    用實用性兼顧個性來形容大眾蔚領一點不為過,它旅行車的車身結構讓車尾的造型異常獨特,同時這樣的車身設計也帶來了極其實用的後備廂空間。

    蔚領的後備廂容積為589L,放倒後排座椅后儲物空間能擴展為1507L!雖然蔚領的軸距為2614mm,與傑德的2760mm軸距有較大差異,但是儲物空間依然表現不錯。

    另外,蔚領還標配了全景天窗、車頂行李架、定速巡航、後排座椅比例放倒、胎壓監測、ESp等與長途自駕旅行相關的配置,可以說是比較厚道的!

    編者點評:

    蔚領的造型時尚也不失穩重,雖然車身尺寸比傑德要小,但是它配置更厚道、價格稍低,在性價比方面也有不錯的表現!

    東風日產-啟辰T90

    指導價:10.98-15.48萬

    說到個性,如今關注度極高的啟辰T90是不能落下的,它定位為中型SUV,採用了與寶馬、奔馳、謳歌等豪華品牌類似的轎跑式SUV設計風格。2765mm的軸距、4793mm的車身長度讓它的側面造型修長而且非常動感!

    而在動力方面,它搭載一款代號為MR20的2.0L自然吸氣發動機,搭配6擋手動/CVT無級變速箱,在動力方面固然無法跟售價數倍於它的豪華品牌SUV相提並論,但是啟辰T90駕駛起來動力充足,平順性很不錯,所以也適合家用。

    編者點評:

    啟辰T90的定位獨特,而且售價比較接地氣,這導致在同價位中基本找不到直接的競爭對手!它除了外觀個性外,在做工、用料方面都表現不錯,值得大家多多考慮。

    眾泰汽車-眾泰SR9

    指導價:10.88-16.18萬

    在這個看臉的時代,一個帥氣的外觀是必不可少的。眾泰SR9的外觀設計不用我多說,非常運動時尚,也給你很保時捷的感覺,而至於這樣的造型大家是否能接受呢?

    眾泰SR9的車身長寬高為4744*1929*1647mm,軸距達2850mm,定位是中型SUV,這樣的車身尺寸跟本田冠道有得一拼。

    它搭載一款來自三菱的2.0T渦輪增壓發動機,最大功率190馬力、峰值扭矩250牛米/2400-4800轉,動力參數在同級別車型裏面屬於中等水平,匹配6速濕式雙離合變速箱或5擋手動變速箱。

    編者點評:

    現在關於眾泰SR9的爭議不少,不過正是由於這些爭議,讓SR9的關注度一直居高不下,而之前推出的SR7也有着不錯的銷量。綜合來看,眾泰SR9的空間、配置、性價比表現不錯,在SUV車型銷量火爆的前提下,它有可能成為市面上悶聲發大財的車型之一!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

  • 最近學習了限流相關的算法

    最近學習了限流相關的算法

    最近測試team在測試過程中反饋部分接口需要做一定的限流措施,剛好我也回顧了下限流相關的算法。常見限流相關的算法有四種:計數器算法, 滑動窗口算法, 漏桶算法, 令牌桶算法

    1.計數器算法(固定窗口)

     計數器算法是使用計數器在周期內累加訪問次數,當達到設定的閾值時就會觸發限流策略。下一個周期開始時,清零重新開始計數。此算法在單機和分佈式環境下實現都非常簡單,可以使用Redis的incr原子自增和線程安全即可以實現

     這個算法常用於QPS限流和統計訪問總量,對於秒級以上周期來說會存在非常嚴重的問題,那就是臨界問題,如下圖:

     假設我們設置的限流策略時1分鐘限制計數100,在第一個周期最後5秒和第二個周期的開始5秒,分別計數都是88,即在10秒時間內計數達到了176次,已經遠遠超過之前設置的閾值,由此可見,計數器算法(固定窗口)限流方式對於周期比較長的限流存在很大弊端。

     Java 實現計數器(固定窗口):

    package com.brian.limit;
    
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicInteger;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 固定窗口
     */
    @Slf4j
    public class FixWindow {
    
        private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
    
        private final int limit = 100;
    
        private AtomicInteger currentCircleRequestCount = new AtomicInteger(0);
    
        private AtomicInteger timeCircle = new AtomicInteger(0);
    
        private void doFixWindow() {
            scheduledExecutorService.scheduleWithFixedDelay(() -> {
                log.info(" 當前時間窗口,第 {} 秒 ", timeCircle.get());
                if(timeCircle.get() >= 60) {
                    timeCircle.set(0);
                    currentCircleRequestCount.set(0);
                    log.info(" =====進入新的時間窗口===== ");
                }
                if(currentCircleRequestCount.get() > limit) {
                    log.info("觸發限流策略,當前窗口累計請求數 : {}", currentCircleRequestCount);
                } else {
                    final int requestCount = (int) ((Math.random() * 5) + 1);
                    log.info("當前發出的 ==requestCount== : {}", requestCount);
                    currentCircleRequestCount.addAndGet(requestCount);
                }
               timeCircle.incrementAndGet();
            }, 0, 1, TimeUnit.SECONDS);
        }
    
        public static void main(String[] args) {
            new FixWindow().doFixWindow();
        }
        
    }

    2.滑動窗口算法

     滑動窗口算法是將時間周期拆分成N個小的時間周期,分別記錄小周期裏面的訪問次數,並且根據時間的滑動刪除過期的小周期。如下圖,假設時間周期為1分鐘,將1分鐘再分為2個小周期,統計每個小周期的訪問數量,則可以看到,第一個時間周期內,訪問數量為92,第二個時間周期內,訪問數量為104,超過100的訪問則被限流掉了。

     

     由此可見,當滑動窗口的格子劃分的越多,那麼滑動窗口的滾動就越平滑,限流的統計就會越精確。此算法可以很好的解決固定窗口算法的臨界問題。

      Java實現滑動窗口:

    package com.brian.limit;
    
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicInteger;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 滑動窗口
     * 
     * 60s限流100次請求
     */
    @Slf4j
    public class RollingWindow {
    
        private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
    
        // 窗口跨度時間60s
        private int timeWindow = 60;
    
        // 限流100個請求
        private final int limit = 100;
    
        // 當前窗口請求數
        private AtomicInteger currentWindowRequestCount = new AtomicInteger(0);
    
        // 時間片段滾動次數
        private AtomicInteger timeCircle = new AtomicInteger(0);
    
        // 觸發了限流策略后等待的時間
        private AtomicInteger waitTime = new AtomicInteger(0);
    
        // 在下一個窗口時,需要減去的請求數
        private int expiredRequest = 0;
    
        // 時間片段為5秒,每5秒統計下過去60秒的請求次數
        private final int slidingTime = 5;
    
        private ArrayBlockingQueue<Integer> slidingTimeValues = new ArrayBlockingQueue<>(11);
    
        public void rollingWindow() {
            scheduledExecutorService.scheduleWithFixedDelay(() -> {
    
                if (waitTime.get() > 0) {
                    waitTime.compareAndExchange(waitTime.get(), waitTime.get() - slidingTime);
                    log.info("=====當前滑動窗口===== 限流等待下一個時間窗口倒計時: {}s", waitTime.get());
                    if (currentWindowRequestCount.get() > 0) {
                        currentWindowRequestCount.set(0);
                    }
                } else {
                    final int requestCount = (int) ((Math.random() * 10) + 7);
                    if (timeCircle.get() < 12) {
                        timeCircle.incrementAndGet();
                    }
                    
                log.info("當前時間片段5秒內的請求數: {} ", requestCount);
                currentWindowRequestCount.addAndGet(requestCount);
                log.info("=====當前滑動窗口===== {}s 內請求數: {} ", timeCircle.get()*slidingTime , currentWindowRequestCount.get());
    
                if(!slidingTimeValues.offer(requestCount)){
                    expiredRequest =  slidingTimeValues.poll();
                    slidingTimeValues.offer(requestCount);
                } 
    
                if(currentWindowRequestCount.get() > limit) {
                    // 觸發限流
                    log.info("=====當前滑動窗口===== 請求數超過100, 觸發限流,等待下一個時間窗口 ");
                    waitTime.set(timeWindow);
                    timeCircle.set(0);
                    slidingTimeValues.clear();
                } else {
                    // 沒有觸發限流,滑動下一個窗口需要,移除相應的:在下一個窗口時,需要減去的請求數
                    log.info("=====當前滑動窗口===== 請求數 <100, 未觸發限流,當前窗口請求總數: {},即將過期的請求數:{}"
                            ,currentWindowRequestCount.get(), expiredRequest);
                    currentWindowRequestCount.compareAndExchange(currentWindowRequestCount.get(), currentWindowRequestCount.get() - expiredRequest);
                }
            }   
            }, 5, 5, TimeUnit.SECONDS);
        }
    
        public static void main(String[] args) {
            new RollingWindow().rollingWindow();
        }
        
    
    }

    計數器(固定窗口)和滑動窗口區別:

    計數器算法是最簡單的算法,可以看成是滑動窗口的低精度實現。滑動窗口由於需要存儲多份的計數器(每一個格子存一份),所以滑動窗口在實現上需要更多的存儲空間。也就是說,如果滑動窗口的精度越高,需要的存儲空間就越大。

    3.漏桶算法

     漏桶算法是訪問請求到達時直接放入漏桶,如當前容量已達到上限(限流值),則進行丟棄(觸發限流策略)。漏桶以固定的速率進行釋放訪問請求(即請求通過),直到漏桶為空。

     Java實現漏桶:

    package com.brian.limit;
    
    import java.util.concurrent.*;
    
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * 漏桶算法
     */
    @Slf4j
    public class LeakyBucket {
        private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
    
        // 桶容量
        public  int capacity = 1000;
        
        // 當前桶中請求數
        public int curretRequest = 0;
    
        // 每秒恆定處理的請求數
        private final int handleRequest = 100;
    
        public void doLimit() {
            scheduledExecutorService.scheduleWithFixedDelay(() -> {
                final int requestCount = (int) ((Math.random() * 200) + 50);
                if(capacity > requestCount){
                    capacity -= requestCount;
                    log.info("<><>當前1秒內的請求數:{}, 桶的容量:{}", requestCount, capacity);
                    if(capacity <=0) {
                        log.info(" =====觸發限流策略===== ");
                    } else {
                        capacity += handleRequest;
                        log.info("<><><><>當前1秒內處理請求數:{}, 桶的容量:{}", handleRequest, capacity);
                    }
                } else {
                    log.info("<><><><>當前請求數:{}, 桶的容量:{},丟棄的請求數:{}", requestCount, capacity,requestCount-capacity);
                    if(capacity <= requestCount) {
                        capacity = 0;
                    }
                    capacity += handleRequest;
                    log.info("<><><><>當前1秒內處理請求數:{}, 桶的容量:{}", handleRequest, capacity);
                }
            }, 0, 1, TimeUnit.SECONDS);
        }
    
        public static void main(String[] args) {
            new LeakyBucket().doLimit();
        }
    }

     漏桶算法有個缺點:如果桶的容量過大,突發請求時也會對後面請求的接口造成很大的壓力。

    4.令牌桶算法

     令牌桶算法是程序以恆定的速度向令牌桶中增加令牌,令牌桶滿了之後會丟棄新進入的令牌,當請求到達時向令牌桶請求令牌,如獲取到令牌則通過請求,否則觸發限流策略。

     

     Java實現令牌桶:

    package com.brian.limit;
    
    import java.util.concurrent.*;
    
    import lombok.extern.slf4j.Slf4j;
    /**
     * 令牌桶算法
     */
    @Slf4j
    public class TokenBucket {
        private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);
    
        // 桶容量
        public  int capacity = 1000;
        
        // 當前桶中請求數
        public int curretToken = 0;
    
        // 恆定的速率放入令牌
        private final int tokenCount = 200;
    
        public void doLimit() {
            scheduledExecutorService.scheduleWithFixedDelay(() -> {
                
                new Thread( () -> {
                    if(curretToken >= capacity) {
                        log.info(" =====桶中的令牌已經滿了===== ");
                        curretToken = capacity;
                    } else {
                        if((curretToken+tokenCount) >= capacity){
                          log.info(" 當前桶中的令牌數:{},新進入的令牌將被丟棄的數: {}",curretToken,(curretToken+tokenCount-capacity));
                          curretToken = capacity;
                      } else {
                          curretToken += tokenCount;
                      }
                    }
                }).start();
    
                new Thread( () -> {
                    final int requestCount = (int) ((Math.random() * 200) + 50);
                    if(requestCount >= curretToken){
                        log.info(" 當前請求數:{},桶中令牌數: {},將被丟棄的請求數:{}",requestCount,curretToken,(requestCount - curretToken));
                        curretToken = 0;
                    } else {
                        log.info(" 當前請求數:{},桶中令牌數: {}",requestCount,curretToken);
                        curretToken -= requestCount;
                    }
                }).start();
            }, 0, 500, TimeUnit.MILLISECONDS);
        }
    
        public static void main(String[] args) {
            new TokenBucket().doLimit();
        }
        
    }

    漏桶算法和令牌桶算法區別:

    令牌桶可以用來保護自己,主要用來對調用者頻率進行限流,為的是讓自己不被打垮。所以如果自己本身有處理能力的時候,如果流量突發(實際消費能力強於配置的流量限制),那麼實際處理速率可以超過配置的限制。而漏桶算法,這是用來保護他人,也就是保護他所調用的系統。主要場景是,當調用的第三方系統本身沒有保護機制,或者有流量限制的時候,我們的調用速度不能超過他的限制,由於我們不能更改第三方系統,所以只有在主調方控制。這個時候,即使流量突發,也必須捨棄。因為消費能力是第三方決定的。
    總結起來:如果要讓自己的系統不被打垮,用令牌桶。如果保證被別人的系統不被打垮,用漏桶算法

     

    參考博客:https://blog.csdn.net/weixin_41846320/article/details/95941361

         https://www.cnblogs.com/xuwc/p/9123078.html

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

    【其他文章推薦】

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

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

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

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

    ※回頭車貨運收費標準