一文讀懂:防止過擬合的所有方法

什麼是過擬合

過擬合就是在訓練集上表現得非常好,在測試集上表現得不好。也就是我們俗稱的泛化能力弱

過擬合無法避免,只能緩解,那麼如何緩解呢?方法太多了。這篇文章一一介紹。

數據集增強Augmentation

圖像上,翻轉,平移,縮放,旋轉,鏡像,增強對比度,增強亮度等諸多方式。
我在下面的內容中介紹了圖像處理的圖像增強的方法:
【預處理庫函數】albumentations庫的簡單了解和使用

Early Stopping

訓練模型的時候,訓練誤差往往是不斷下降的,但是驗證數據集的誤差,是先下降後上升。 兩個數據集的誤差出現分歧的時候,說明模型開始過擬合了。所以Early Stopping就是當驗證數據集的誤差不在下降的時候,結束訓練,保存模型參數。

正則化regularization

L1正則:模型中只有少部分特徵對模型的泛化能力有貢獻,所以L1就是限制模型中非零參數的數量。讓大部分的模型參數都是0,只有真正對泛化能力其作用的參數才是非零的。

L2正則:我們希望模型找到的極小值是平坦的,為什麼呢?

圖中表示的意思,就是平坦的極小值,可以有更多的容忍,容忍什麼呢?容忍訓練數據集和測試數據集之前的分佈偏差。現在,如果模型的某些參數特別大,那麼就算輸入的樣本只有很小的區別,但是經過特別大的參數之後,模型給出的結果很可能是非常不同的。這就是太陡峭。所以L2正則就是限制模型參數的大小。參數的平方的和作為損失的一部分,當參數數值越大,那麼梯度下降的損失越大,就會強迫參數變小。

這裡有兩幅圖:

這一幅圖體現的是假設只有兩個參數的情況下,增加L1正則的情況。圓圈圈體現的是損失等值線,方框是L1正則的損失。假設沒有L1正則,那麼參數應該收斂到最小的那個圓圈中的。但是因為增加了L1正則,所以需要權衡兩個部分的損失,然後找到接觸的交點位置。因為圓形和矩形在矩形的頂點相交的可能性大,而矩形的頂點就是某一個參數為0的情況。所以L1正則會讓模型參數有更大的可能性為0.
【在更多參數的模型中,會有更多的頂點。不過二維圖像就畫不出來了】

這個是L2正則的示意圖。L2正則式一個原型因為是參數的平方和。相比L1的(0,1)這樣的交點,L2更希望每一個參數都普遍較小,不希望某一個參數特別大。

Dropout

這個就是神經網絡中,在全連接網絡中經常用到的。

在每一個Batch數據訓練的時候,Dropout層按照概率P隨機讓一些神經元失活,然後保留下來的神經元的參數被更新。dropout是只有在訓練的時候才使用的,在測試的時候並不適用。

我個人理解的dropout其實就相當於一個多模型融合的過程。因為每一次都會失活一部分的神經元,所以每一次的模型都是不那麼一樣的,相當於不同的模型吧。

增加噪音

輸入中增加噪音

輸入中有噪音\(\epsilon\),那麼輸出中就會有一個類似於\(\epsilon \omega\),這樣的損失項。 從而限制權值的大小。

當然這樣也可以增加模型對輸入的容忍度,我覺得也可以理解為一種數據增強。 去噪自編碼器DAE就是利用這樣的方法的。

權值中加噪音

這個用的不多,在初始化網絡的時候,用0均值的高斯分佈作為參數的初始化。

集成

集成主要是bagging,boosting,之前說的dropout我覺得也可以算作集成的方法

bagging

將數據集抽取一部分,比如抽取70%的樣本,然後用這些樣本去訓練一個模型。然後再從數據集中抽取70%的樣本,再訓練一個新的。典型的就是隨機森林。
【神經網絡因為訓練速度的問題,所以一般不用這樣的方法。決策樹lgb啥的可以用】

boosting

訓練複雜神經網絡比較慢,所以可以通過訓練多個簡單的分類器,然後加權平均每一個分類器的輸出。這就是Boost的思想。【這句話給我背下來!】

之後整理一下Adaboost和XGBoost的這些算法。

其他

  • 限制網絡的層數和複雜度

**END**

喜歡的話請關注我們的微信公眾號~《你好世界煉丹師》或者知乎【你好世界煉丹師】。

  • 公眾號主要講統計學,數據科學,機器學習,深度學習,以及一些參加Kaggle競賽的經驗。
  • 公眾號內容建議作為課後的一些相關知識的補充,飯後甜點。
  • 此外,為了不過多打擾,公眾號每周推送一次,每次4~6篇精選文章。

微信搜索公眾號:你好世界煉丹師。期待您的關注。

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

北加州野火再起安全性斷電 2000多人撤離

摘錄自2019年10月24日中央社報導

北加州酒鄉有超過一萬公頃的土地遭到森林野火侵襲,2000多人被迫離家,目前沒有人員傷亡,以公共安全為理由的民生斷電又展開。美國媒體ABC News在推特放上現場畫面。

這場名為金凱德(Kincade)的火災,起火地點距離太平洋瓦斯電力公司(Pacific Gas and Electric Company,PG&E)切斷電源的索諾馬郡(Sonoma County)不遠,索諾馬是與納帕山谷(Napa Valley)齊名的北加州葡萄酒鄉。

今年季節性的高熱乾風再度來襲,PG&E近日兩度以公共安全為考量,在非常短的時間、突襲式通知警戒區域的民眾斷電消息,引發擾民爭議,也被批評沒有及早檢查、更新輸電線等相關設備。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

程泰打入Tesla Model 3供應鏈,預計年底出貨

程泰16日參加宏遠證券所舉辦自動化產業投資論壇,對於2017年營運展望,程泰表示,目前母公司程泰集團在手訂單已回升至5億多元,且未含3月台北國際工具機展部分,同時在中國大陸訂單約有2-3億元,整體而言,目前整體訂單能見度看到第二季底。程泰集團董事長楊德華則表示,集團整體在手訂單規模約22億至23億元,至於3月台北國際工具機展接單規模約在3億至5億元。

程泰也指出,公司業務以車床為主,由於波音預期未來20年全球航太市場規模將達180兆元,預估民航機需求約3.8萬架,加上政府要推國機國造,因此未來也會聚焦在航太產業。此外,在電動車布局方面,程泰打入美國特斯拉電動車Model 3供應鏈,於台中大里工業區建立代工基地,預計今年底起,每月將出貨減速齒輪箱的齒輪傳動軸前端車銑磨6萬套。

程泰集團旗下包括程泰及亞崴兩工具機廠,亞崴表示,農曆年後,台灣航太及汽車零組件業開始投資擴廠,加上推進國機國造及航太零件國產化,預期今年台灣市場會有不錯表現,目前台灣地區在手訂單14億元,大陸吳江及嘉興廠在手訂單3-4億元,兩岸合併在手訂單約18億元,訂單能見度看到7月;在產能布局方面,嘉義大埔美一期新廠預計今年底完工,大陸吳江廠亦規劃最快於今年底興建二期廠。

(本文內容由授權使用。圖片出處:Tesla)

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

博弈論——兩人取子遊戲與威佐夫博弈,隱藏在背後的黃金分割

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

今天是算法和數據結構專題第25篇文章,我們繼續博弈論專題。

在上一篇文章當中我們了解了最簡單的巴什博奕,今天我們來看看另一個經典的博弈模型——威佐夫博弈。博弈論和機器學習有些類似,數學家們針對場景進行建模,設計出了幾個經典模型。然後我們在面臨具體問題的時候,對問題進行深入分析,尋找最合適的模型應用來解決它。

石子問題

我們來看一道經典的例題,有兩堆石子,有兩個絕頂聰明的人在玩一個遊戲。每次每個人可以從其中一堆石子當中取走任意數量的石子,或者是從兩堆當中同時取走相同數量的石子。無法取石子的人落敗,請問,在告知兩堆石子數量的情況下,這兩個人當中哪一方會獲勝?

我們簡單分析一下,會發現一些局面是先手必敗的。比如說(0, 0),再比如(1, 2)。我們簡單分析一下(1, 2),先手有4種策略,首先他可以取走第一堆,那麼後手可以取完第二堆,顯然後手獲勝。他也可以在第二堆當中取1個,這時剩下(1, 1),後手會同時取完,同樣是後手獲勝。第三種是他取走第二堆,後手可以取完第一堆,後手獲勝。第四種是他在第一堆和第二堆當中同時取走一個,這時第二堆剩下一個,後手勝。

那麼,這些必敗的狀態之間有什麼規律呢?我們怎麼找到這個規律,並且找到解呢?

分析

我們可以枚舉幾個必敗的狀態:(0, 0), (1, 2), (3, 5), (4, 7)…

我們觀察一下這些狀態,可以找到兩條規律。我們假設從小到大排的第k個必敗狀態是(x, y),並且x < y。我們可以發現y = x + k。也就是說必敗狀態兩個數的差值是遞增的,這也說明了每一個必敗狀態的差值都各不相同。

其實這是很容易證明的,我們用反證法,假設(a, a+k), (b, b+k)都是必敗狀態,並且a < b。那麼先手在面臨(b, b+k)的時候,只需要在兩堆當中同時取走b-a個石子,那麼給後手的局面就是(a, a+k)。對於後手來說,這是一個必敗的局面,這就和(b, b+k)先手必敗矛盾,所以不存在兩個必敗局面的差值相等

我們也可以作圖分析,我們把兩堆石子的數量看成是坐標軸上的一個點。所以遊戲就變成了:棋盤上有一個點,每次每個人可以將它向下、向左或者向左下移動若干個格子,不能移動的人輸。終止節點顯然是原點,一步就能移動到原點的點顯然是必勝點,假設我們給這些所有必勝點都染色的話,剩下的的沒當中橫縱坐標和最小的點就是下一個必敗點。因為它不論如何移動,都會給對手留下一個必勝點。

我們根據上面的邏輯把必敗點都染色,可以得到下面這張圖:

從這張圖可以看出,必敗點之間不能通過一次移動得到,換句話說可以一次移動到必敗點的點都是必勝點,從圖上可以看出,除了必敗點的之外的點都是必勝點,並且每一個自然數都必然只會被包含在一個必敗狀態當中。

到這裏,我們距離解法已經很接近了,現在剩下的問題是,我們如何根據x和y的取值快速判斷它們是否構成一個必敗局面呢?也就是說我們能不能找出一個通項公式,對於第k個必敗局面,它的坐標是(\(x_k, y_k\))呢?

求解

為了寫出通項公式,我們需要引入Betty定理

設a和b是兩個正無理數,並且\(\frac{1}{a} + \frac{1}{b} = 1\)

記P={[\(a_n\)], \(n \in N^+\)}, Q={[\(b_n\)], \(n \in N^+\)},則\(P \cap Q = \varnothing\)\(P\cup Q = N^+\)

證明

\(P\cap Q = \varnothing\)

反證,我們假設存在\(k \in P\)並且\(k \in Q\),即存在正整數n, m滿足 k < an, bm < k+1。

也就是:\(\frac{n}{k} > \frac{1}{a} > \frac{n}{k+1}, \frac{m}{k} > \frac{1}{b} > \frac{m}{k+1}\)兩個式子相加可以得到:\(\frac{m+n}{k} > 1 > \frac{m+n}{k}\)

\(k < n+m < k+ 1\),這與n,m,k都是正整數矛盾

\(P \cup Q = N^+\)

反證,假設存在\(k \notin P\)\(k \notin Q\),即存在正整數n,m滿足\(an < k < a(n+1)-1, bm < k < b(m+1)-1\)

即:\(\frac{n}{k} < \frac{1}{a} < \frac{n+1}{k+1}, \frac{m}{k} < \frac{1}{b} < \frac{m+1}{k+1}\)

相加,可以得到:\(\frac{m+n}{k} < 1 < \frac{n+m+2}{k+1}\)

即:n + m < k < n + m + 1,這與n,m,k均為正整數矛盾

我們花了這麼大力氣來證明Betty定理就是為了用的,因為我們發現必敗狀態的通項和Betty定理序列很像。我們不妨假設存在這樣的a, b同時滿足Betty定理與必敗狀態的性質:

\[[an] + n = [bn], \frac{1}{a} + \frac{1}{b} = 1 \]

\[[an] + n = [an + n] = [(a+1)n] = [bn] \]

代入可以得到:

\[\frac{1}{a} + \frac{1}{a+1} = 1 \]

解這個方程,可以得到\(a = \frac{1 + \sqrt{5}}{2}\approx 1.618\),熟悉數學的同學相信一下就看出來了,這個數是黃金分割的比例,這是巧合嗎,還是藏着更深的道理呢?

至少,求出了a之後,我們就可以非常簡單地判斷必敗狀態了:

import math
def lose_or_win(a, b):
    if a > b:
        a, b = b, a
    
    k = b - a
    # 根據差值k求出第k個必敗狀態,判斷是否相等
    return not (int(k * (math.sqrt(5)+1) / 2)) == a

總結

和之前介紹的巴什博奕相比,威佐夫博弈的推導過程要複雜得多,但是雖然推導過程依然複雜,但是仍然擋不住最後實現的代碼非常簡單。

另外,在推導的過程當中,我們用到了Betty定理,這個定理的推導和證明雖然不難,但是如果不是數學專業的同學,可能大概率都沒有接觸過。這其實體現了博弈論本身和數學的關係是非常緊密的。一個看起來非常簡單的問題,引申出了一系列眼花繚亂的推導和證明,怎麼樣,大家看得還過癮嗎?

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

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

這一次搞懂Spring Web零xml配置原理以及父子容器關係

前言

在使用Spring和SpringMVC的老版本進行開發時,我們需要配置很多的xml文件,非常的繁瑣,總是讓用戶自行選擇配置也是非常不好的。基於約定大於配置的規定,Spring提供了很多註解幫助我們簡化了大量的xml配置;但是在使用SpringMVC時,我們還會使用到WEB-INF/web.xml,但實際上我們是完全可以使用Java類來取代xml配置的,這也是後來SpringBoott的實現原理。本篇就來看看Spring是如何實現完全的零XML配置。

正文

先來看一下原始的web.xml配置:

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
      <!--加載spring配置-->
      classpath:spring.xml
    </param-value>
  </context-param>
  <context-param>
    <param-name>webAppRootKey</param-name>
    <param-value>ServicePlatform.root</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    <!--<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>-->
  </listener>

  <servlet>
    <servlet-name>spring-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!--springmvc的配置文件-->
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:spring-dispatcher.xml</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>spring-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
</web-app>

這裏各個配置的作用簡單說下,context-param是加載我們主的sping.xml配置,比如一些bean的配置和開啟註解掃描等;listener是配置監聽器,Tomcat啟動會觸發監聽器調用;servlet則是配置我們自定義的Servlet實現,比如DispatcherServlet。還有其它很多配置就不一一說明了,在這裏主要看到記住context-paramservlet配置,這是SpringIOC父子容器的體現。在之前的I文章中講過IOC容器是以父子關係組織的,但估計大部分人都不能理解,除了看到複雜的繼承體系,並沒有看到父容器作用的體現,稍後來分析。
了解了配置,我們就需要思考如何替換掉這些繁瑣的配置。實際上Tomcat提供了一個規範,有一個ServletContainerInitializer接口:

public interface ServletContainerInitializer {
    void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}

Tomcat啟動時會調用該接口實現類的onStartup方法,這個方法有兩個參數,第二個不用說,主要是第一個參數什麼?從哪裡來?另外我們自定義的實現類又怎麼讓Tomcat調用呢?
首先解答最後一個問題,這裏也是利用SPI來實現的,因此我們實現了該接口后,還需要在META-INF.services下配置。其次,這裏傳入的第一個參數也是我們自定義的擴展接口的實現類,我們可以通過我們自定義的接口實現很多需要在啟動時做的事,比如加載Servlet,但是Tomcat又是怎麼知道我們自定義的接口是哪個呢?這就需要用到@HandlesTypes註解,該註解就是標註在ServletContainerInitializer的實現類上,其值就是我們擴展的接口,這樣Tomcat就知道需要傳入哪個接口實現類到這個onStartup方法了。來看一個簡單的實現:

@HandlesTypes(LoadServlet.class)
public class MyServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) throws ServletException {
        Iterator var4;
        if (set != null) {
            var4 = set.iterator();
            while (var4.hasNext()) {
                Class<?> clazz = (Class<?>) var4.next();
                if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers()) && LoadServlet.class.isAssignableFrom(clazz)) {
                    try {
                        ((LoadServlet) clazz.newInstance()).loadOnstarp(servletContext);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

public interface LoadServlet {

    void loadOnstarp(ServletContext servletContext);
}

public class LoadServletImpl implements LoadServlet {
    @Override
    public void loadOnstarp(ServletContext servletContext) {
        ServletRegistration.Dynamic initServlet = servletContext.addServlet("initServlet", "org.springframework.web.servlet.DispatcherServlet");
        initServlet.setLoadOnStartup(1);
        initServlet.addMapping("/init");
	}
}

這就是Tomcat給我們提供的規範,通過這個規範我們就能實現Spring的零xml配置啟動,直接來看Spring是如何做的。
根據上面所說我們可以在spring-web工程下找到META-INF/services/javax.servlet.ServletContainerInitializer配置:

@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
	@Override
	public void onStartup(@Nullable Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
			throws ServletException {

		List<WebApplicationInitializer> initializers = new LinkedList<>();

		if (webAppInitializerClasses != null) {
			for (Class<?> waiClass : webAppInitializerClasses) {
				// Be defensive: Some servlet containers provide us with invalid classes,
				// no matter what @HandlesTypes says...
				if (!waiClass.isInterface() && !Modifier.isAbstract(waiClass.getModifiers()) &&
						WebApplicationInitializer.class.isAssignableFrom(waiClass)) {
					try {
						initializers.add((WebApplicationInitializer)
								ReflectionUtils.accessibleConstructor(waiClass).newInstance());
					}
					catch (Throwable ex) {
						throw new ServletException("Failed to instantiate WebApplicationInitializer class", ex);
					}
				}
			}
		}

		if (initializers.isEmpty()) {
			servletContext.log("No Spring WebApplicationInitializer types detected on classpath");
			return;
		}

		servletContext.log(initializers.size() + " Spring WebApplicationInitializers detected on classpath");
		AnnotationAwareOrderComparator.sort(initializers);
		for (WebApplicationInitializer initializer : initializers) {
			initializer.onStartup(servletContext);
		}
	}

}

核心的實現就是WebApplicationInitializer,先看看其繼承體系

AbstractReactiveWebInitializer不用管,主要看另外一邊,但是都是抽象類,也就是說真的實例也是由我們自己實現,但需要我們實現什麼呢?我們一般直接繼承AbstractAnnotationConfigDispatcherServletInitializer類,有四個抽象方法需要我們實現:

    //父容器
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{SpringContainer.class};
    }

    //SpringMVC配置子容器
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{MvcContainer.class};
    }

    //獲取DispatcherServlet的映射信息
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

	// filter配置
    @Override
    protected Filter[] getServletFilters() {
        MyFilter myFilter = new MyFilter();
        CorsFilter corsFilter = new CorsFilter();
        return new Filter[]{myFilter,corsFilter};
    }

這裏主要注意getRootConfigClassesgetServletConfigClasses方法,分別加載父、子容器:

@ComponentScan(value = "com.dark",excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
})
public class SpringContainer {
}

@ComponentScan(value = "com.dark",includeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = {Controller.class})
},useDefaultFilters = false)
public class MvcContainer {
}

看到這兩個類上的註解應該不陌生了吧,父容器掃描裝載了所有不帶@Controller註解的類,子容器則相反,但需要對象時首先從當前容器中找,如果沒有則從父容器中獲取,為什麼要這麼設計呢?直接放到一個容器中不行么?先思考下, 稍後解答。
回到onStartup方法中,直接回調用到AbstractDispatcherServletInitializer類:

	public void onStartup(ServletContext servletContext) throws ServletException {
		super.onStartup(servletContext);
		//註冊DispatcherServlet
		registerDispatcherServlet(servletContext);
	}

先是調用父類:

	public void onStartup(ServletContext servletContext) throws ServletException {
		registerContextLoaderListener(servletContext);
	}

	protected void registerContextLoaderListener(ServletContext servletContext) {

		//創建spring上下文,註冊了SpringContainer
		WebApplicationContext rootAppContext = createRootApplicationContext();
		if (rootAppContext != null) {
			//創建監聽器
			ContextLoaderListener listener = new ContextLoaderListener(rootAppContext);
			listener.setContextInitializers(getRootApplicationContextInitializers());
			servletContext.addListener(listener);
		}
	}

然後調用createRootApplicationContext創建父容器:

	protected WebApplicationContext createRootApplicationContext() {
		Class<?>[] configClasses = getRootConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
			context.register(configClasses);
			return context;
		}
		else {
			return null;
		}
	}

可以看到就是創建了一個AnnotationConfigWebApplicationContext對象,並將我們的配置類SpringContainer註冊了進去。接着創建Tomcat啟動加載監聽器ContextLoaderListener,該監聽器有一個contextInitialized方法,會在Tomcat啟動時調用。

	public void contextInitialized(ServletContextEvent event) {
		initWebApplicationContext(event.getServletContext());
	}

	 */
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
		long startTime = System.currentTimeMillis();
		try {
			// Store context in local instance variable, to guarantee that
			// it is available on ServletContext shutdown.
			if (this.context == null) {
				this.context = createWebApplicationContext(servletContext);
			}
			if (this.context instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;
				if (!cwac.isActive()) {
					// The context has not yet been refreshed -> provide services such as
					// setting the parent context, setting the application context id, etc
					if (cwac.getParent() == null) {
						// The context instance was injected without an explicit parent ->
						// determine parent for root web application context, if any.
						ApplicationContext parent = loadParentContext(servletContext);
						cwac.setParent(parent);
					}
					configureAndRefreshWebApplicationContext(cwac, servletContext);
				}
			}
			servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

			ClassLoader ccl = Thread.currentThread().getContextClassLoader();
			if (ccl == ContextLoader.class.getClassLoader()) {
				currentContext = this.context;
			}
			else if (ccl != null) {
				currentContextPerThread.put(ccl, this.context);
			}

			return this.context;
		}
	}

可以看到就是去初始化容器,這個和之前分析xml解析是一樣的,主要注意這裏封裝了ServletContext對象,並將父容器設置到了該對象中。
父容器創建完成后自然就是子容器的創建,來到registerDispatcherServlet方法:

	protected void registerDispatcherServlet(ServletContext servletContext) {
		String servletName = getServletName();
		Assert.hasLength(servletName, "getServletName() must not return null or empty");

		//創建springmvc的上下文,註冊了MvcContainer類
		WebApplicationContext servletAppContext = createServletApplicationContext();
		Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

		//創建DispatcherServlet
		FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
		Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
		dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

		ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
		if (registration == null) {
			throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
					"Check if there is another servlet registered under the same name.");
		}

		/*
		* 如果該元素的值為負數或者沒有設置,則容器會當Servlet被請求時再加載。
			如果值為正整數或者0時,表示容器在應用啟動時就加載並初始化這個servlet,
			值越小,servlet的優先級越高,就越先被加載
		* */
		registration.setLoadOnStartup(1);
		registration.addMapping(getServletMappings());
		registration.setAsyncSupported(isAsyncSupported());

		Filter[] filters = getServletFilters();
		if (!ObjectUtils.isEmpty(filters)) {
			for (Filter filter : filters) {
				registerServletFilter(servletContext, filter);
			}
		}

		customizeRegistration(registration);
	}

	protected WebApplicationContext createServletApplicationContext() {
		AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
		Class<?>[] configClasses = getServletConfigClasses();
		if (!ObjectUtils.isEmpty(configClasses)) {
			context.register(configClasses);
		}
		return context;
	}

這裏也是創建了一個AnnotationConfigWebApplicationContext對象,不同的只是這裏註冊的配置類就是我們的Servlet配置了。然後創建了DispatcherServlet對象,並將上下文對象設置了進去。看到這你可能會疑惑,既然父子容器創建的都是相同類的對象,何來的父子容器之說?別急,這個在初始化該上文時就明白了。但是這裏的初始化入口在哪呢?沒有看到任何監聽器的創建和調用。實際上這裏的上下文對象初始化是在Servlet初始化時實現的,即init方法,直接來到HttpServletBeaninit方法(分析SpringMVC源碼時講過):

	public final void init() throws ServletException {
		...省略
		
		// Let subclasses do whatever initialization they like.
		initServletBean();
	}

	protected final void initServletBean() throws ServletException {
		try {
			this.webApplicationContext = initWebApplicationContext();
			initFrameworkServlet();
		}
	}

	protected WebApplicationContext initWebApplicationContext() {
		//這裡會從servletContext中獲取到父容器,就是通過監聽器加載的容器
		WebApplicationContext rootContext =
				WebApplicationContextUtils.getWebApplicationContext(getServletContext());
		WebApplicationContext wac = null;

		if (this.webApplicationContext != null) {
			// A context instance was injected at construction time -> use it
			wac = this.webApplicationContext;
			if (wac instanceof ConfigurableWebApplicationContext) {
				ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
				if (!cwac.isActive()) {
					if (cwac.getParent() == null) {
						cwac.setParent(rootContext);
					}
					//容器加載
					configureAndRefreshWebApplicationContext(cwac);
				}
			}
		}
		if (wac == null) {
			wac = findWebApplicationContext();
		}
		if (wac == null) {
			wac = createWebApplicationContext(rootContext);
		}

		if (!this.refreshEventReceived) {
			synchronized (this.onRefreshMonitor) {
				onRefresh(wac);
			}
		}

		if (this.publishContext) {
			// Publish the context as a servlet context attribute.
			String attrName = getServletContextAttributeName();
			getServletContext().setAttribute(attrName, wac);
		}

		return wac;
	}

看到這裏想你也應該明白了,首先從ServletContext中拿到父容器,然後設置到當前容器的parent中,實現了父子容器的組織,而這樣設計好處我想也是很清楚的,子容器目前裝載的都是MVC的配置和Bean,簡單點說就是Controller,父容器中都是Service,Controller是依賴於Service的,如果不構建這樣的層級關係並優先實例化父容器,你怎麼實現Controller層的依賴注入成功呢?

總結

本篇結合之前的文章,分析了SpringMVC零XML配置的實現原理,也補充了之前未分析到父子容器關係,讓我們能從細節上更加全面的理解SpringIOC的實現原理,相信看完本篇對於SpringBoot的實現你也會有自己的想法。

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

流產、死胎層出不窮 南蘇丹隱匿石油業環境報告 犧牲者至今未受保障

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

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

台達電佈局電動車產品,拓歐美電動車市場

台達電於4月28日舉行法說會,公佈2017年第一季財報合併營收489.25億元新台幣,季減14%,年增2.8%;毛利率27.2%,季減0.06個百分點,年增0.26個百分點;因管控得宜,第一季匯兌收益1.4億元;稅後淨利39.19億元,每股盈餘1.51元,低於前一季的1.91元,略高於去年同期的1.50元。

台達電執行長鄭平表示,公司5月起進行組織調整,擴大在電動車相關產品佈局,目前公司已打入歐美電動車廠,提供包括車載充電器、動力馬達、DC-DC轉換器等電動車零組件;在中國大陸也打入合資車廠,雖然電動車領域營收短期內成長幅度不大,但仍看好電動車領域未來長期的布局效益。

台達電宣布組織調整,自2日起,將以「電源及零組件」、「自動化」與「基礎設施」為新三大業務範疇,其中,電源及零組件業務包括電動車方案事業群(EVSBG)、嵌入式電源系統事業群(EPSBG)、商用電源事業群(MPBG)、零組件事業群(CPBG)、風扇暨熱傳導事業群(FMBG);自動化業務包括機電事業群(IABG)、樓宇自動化事業群(BABG);至於基礎設施業務則包括資通訊基礎設施事業群(ICTBG)、能源基礎設施事業群(EISBG)。以新三大業務範疇區分,第一季電源及零組件占營收比重為55%、自動化占比為11%,基礎設施則占31%。

對於未來營運展望,台達電董事長海英俊表示,第二季看好IA、樓宇自動化、數據中心的營運動能;今年雖然有匯率變數,但強調公司營運不會受到影響,並將持續朝向高毛利產品發展,讓毛利率能維持目前水準或更好。

(本文內容由授權使用)  

 

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

【其他文章推薦】

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

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

※回頭車貨運收費標準

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

※超省錢租車方案

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

地球發燒了研究:2080年前熱死人數激增20倍

摘錄自2018年8月1日蘋果日報台北報導

天氣讓人熱得受不了,而這種情況若不加以控制,地球上熱死人的數目將增加20倍。最新研究指出,在各國政府完全不做任何事的最糟情況,部分國家在2080年前,因為高溫導致的死亡數字將增加2000%。

這項刊登在「公共科學圖書館醫學期刊」(PLOS Medicine)的報告指出,歐洲、部分亞洲與北美洲地區的氣溫逐年升高,熱浪造成數以千計的死亡案例。該報告首席研究員、澳洲蒙納士大學(Monash University)副教授郭玉明(Yuming Guo,音譯)說:「未來的熱浪會發生得更頻繁、更強烈,維持時間也更長」、「如果我們找不到減緩氣候變遷的方法,並幫助人民適應熱浪氣候,和熱浪有關的死亡案例將會急遽增加。」

報告中指出,最糟的情況下,哥倫比亞在2031至2080年間被高溫熱死的人數,會比1971年至2010年增加2000%,即增加20倍;菲律賓在2031到2080因熱浪而額外死亡的人數,是1971到2020的12倍;澳洲、美國是5倍,英國是4倍。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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

K8S-磁盤配額管理-整理

1.  ephemeral-storage介紹

Kubernetes在1.8的版本中引入了一種類似於CPU,RAM的新的資源模式:ephemeral-storage屬性(直譯為臨時存儲),並且在1.10的版本kubelet默認啟用了這個特性。

ephemeral-storage實現了對Pod應用存儲資源的管理,可以有效的降低Pod應用失控消耗完 node磁盤空間的風險。官網中對該屬性的描述如下:

(https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/):

 

從上述官網介紹,總結如下:

  1. 臨時存儲:臨時的含義是指容器內的數據未做持久化處理,生命周期和容器一致。
  2. 作用對象:容器日誌(/var/log)、EmptyDir類型的volume數據(/var/lib/kubelet)、鏡像層和容器可寫層(/var/lib/docker)。由此可見,基本覆蓋了Pod各個方面的磁盤消耗。
  3. 管理的文件系統:ephemeral-storage對kubelet的根目錄(默認是/var/lib/kubelet)所在的節點分區(文件系統)進行管理,即如果把/var/lib/docker獨立分區,ephemeral-storage將不對/var/lib/docker目錄進行管理。
  4. Pod調度流程:節點上的kubelet啟動的時候,kubelet會統計當前節點的kubelet分區的可分配的磁盤資源,或者你可以覆蓋節點上kubelet的配置來自定義可分配的資源。在創建Pod時會根據存儲需求調度到滿足存儲的節點,在Pod使用超過限制的存儲時會對其做驅逐的處理來保證不會耗盡節點上的磁盤空間。

2. ephemeral-storage功能驗證

2.1   環境準備

  • 虛擬機配置

1)    規格:16 vCpu + 80 GB RAM + 1000 GB 磁盤

2)    分區:/var/lib/docker、/var/lib/kubelet/和/var/log全在同一個系統分區上

  • 測試容器鏡像

1)  Dockerfile

FROM ubuntu:16.04
ADD start.sh /home/
RUN mkdir -p /lq/log/
ENTRYPOINT /home/start.sh

2)  Start.sh

#!/bin/bash
while true
do
dateString=`date`
echo "$dateString==================================">> /lq/log/test.log
done
  • 集群環境

1)  Kuberneters版本:1.15.6

2)  Docker版本:18.06

2.2   容器可寫層大小

  • 容器的部署文件

 

說明:

1)    容器的啟動腳本start.sh會持續的向容器內路徑/lq/log下寫test.log日誌

2)    該日誌並未掛載出來,故日誌文件在宿主機的容器可寫層目錄下

3)    該容器申請10Mi的磁盤空間,上限為20Mi

  • 創建該Pod

使用kubectl apply -f xxxx.yaml,觀察可寫層日誌大小情況以及Pod運行情況

很快可寫層的日誌就達到了16Mi

  • 繼續觀察Pod

 

Pod驅逐了(容器被殺掉,容器內數據全部丟失)

 從上述Event事件可以看到,Pod可用磁盤空間被限制住了

2.3   EmptyDir日誌

  • 部署文件

 

說明:

1)    容器的啟動腳本start.sh會持續的向容器內路徑/lq/log下寫test.log日誌

2)    該日誌通過EmptyDir掛載出來,故日誌文件在宿主機的/var/lib/kubelet目錄下

3)    該容器申請10Mi的磁盤空間,上限為100Mi,emptyDir路徑上限為40Mi

  • 創建該Pod

使用kubectl apply -f  xxxxxxxx.yaml

  • 查詢日誌路徑以及Pod運行情況

 

 

 

可以看到日誌已經到了32Mi,目前Pod運行正常

  • 繼續等待,觀察Pod情況

 

Pod被驅逐了(容器殺死,全部數據丟失)

  • 查看Pod事件

 

可見該日誌磁盤空間被限制了

2.4   /var/log目錄日誌

一般Pod的啟動日誌(k8s上的控制台日誌)會記錄到宿主機的/var/log目錄下,並且根據前面介紹得知ephemeral-storage會對該目錄下的容器日誌磁盤空間大小進行管理,但是由於我使用的測試鏡像並無啟動日誌,故通過hostPath掛載的方式掛載到該路徑下,看看我們显示指定掛載路徑的時候,ephemeral-storage還能否生效。

  • 部署文件

 

  • 創建該Pod

 

  • 觀察Pod運行情況

 

可以看到,自己显示掛載的路徑並做不到磁盤空間限制。

  • 換一種思路

由於無啟動日誌,故想書寫腳本向容器的啟動日誌瘋狂輸出日誌,觀測Pod運行情況

1)  修改start.sh

 

2)  修改部署文件

 

3)  創建該Pod

 

Docker logs查看,容器在瘋狂的輸出日誌

 

4)  持續一段時間,觀察Pod運行狀態

 

Pod驅逐了

2.5   其他情況說明

  1. /var/lib/docker和/var/lib/docker分區文件系統不一致。未做截圖,但是已經實際驗證:/var/lib/docker的分區和kubelet分區不一致的時候,ephemeral-storage對/var/lib/docker目錄磁盤空間大小不做管控

3.  驗證結論

  1. 分區要一致,否則ephemeral-storage管理不到
  2. ephemeral-storage管理的是容器相關的目錄路徑下的磁盤大小,自己顯式掛載的定製化路徑無法控制磁盤空間

4.  參考文檔

  1. https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
  2. https://blog.csdn.net/sdmei/article/details/101017405(總結的非常好)

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

【其他文章推薦】

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

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

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

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

※回頭車貨運收費標準

nuget 包是如何還原的

nuget 包是如何還原的

Intro

一直以來從來都只是簡單的用 nuget 包,最近想折騰一個東西,需要自己搞一個 nuget 包的解析,用戶指定 nuget 包的名稱和版本,然後去解析對應的 nuget 包並添加引用到項目,
於是就想搞明白 nuget 包是怎麼還原的,對於本地已經下載了的 nuget 包又是怎麼找的

Nuget 包的引用

對於 dotnetcore 項目(這裏不算之前那種 project.json 的項目,只討論 *.csproj 這種項目),都是使用新的項目格式,PackageReference 模式

示例:

<PackageReference Include="WeihanLi.Common" Version="1.0.39" /> 

對於 dotnet framework 項目,如果使用 PackageReference 包格式和上面一樣,如果是傳統的 packages.config 包形式,會有一個 packages.config 的文件包含引用的 nuget 包,文件內容示例:

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
</packages>

本文主要說明 dotnetcore 這種 PackageReference 這種形式

nuget 包的還原

nuget 包在第一次從 nuget.org 或自己的包源上下載之後會存放在本地的一個文件夾中,下次再需要相同版本的包還原時就會直接從本地的包中獲取,而這個保存的文件夾是 nuget 配置的一部分,在網上可以找到一些修改 nuget 默認保存 packages 文件夾的位置,但是這些文章都很類似,都只是給出了一個解決方案然而並沒有說明為什麼要這麼做,這麼做的根據是什麼並沒有說明,其實這種解決方案是添加了一個默認的 nuget 配置文件,修改了 nuget 包保存的位置

nuget 配置

默認配置

nuget 會有一些默認的配置,可以參考官方文檔: https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file#config-section

nuget 配置中有一個 globalPackagesFolder 的配置,是用來指定默認的 nuget 包保存的位置,在 Windows 上默認的保存位置是 %userprofile%\.nuget\packages,在 Linux/Mac 上默認的保存位置是 ~/.nuget/packages,可以使用 nuget.configNuGet.Config 配置文件來修改默認的保存文件,除此之外,還可以通過環境變量的方式,配置 NUGET_PACKAGES 來修改默認 nuget 包保存的位置

默認配置文件

nuget 配置的默認配置文件,官方文檔:https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-config#options

Windows 上默認配置文件的位置是 %AppData%\NuGet\NuGet.Config 這也是現在網上那些修改默認保存 nuget 包位置的解決方案,
Linux/Mac 上大多是 ~/.config/NuGet/NuGet.Config,有的可能是 ~/.nuget/NuGet/NuGet.Config(和系統版本有關係)

Windows 上默認是沒有這個配置文件的,添加這個默認配置文件之後就是全局作用的

創建 %AppData%\NuGet\NuGet.Config 這個默認的配置文件,然後在這個配置文件里配置 globalPackagesFolder 來修改默認的 nuget 包保存路徑

示例:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
  </packageSources>
  <config> 
    <add key="globalPackagesFolder" value="D:\nuget\packages" />
  </config>
</configuration>

Reference

  • https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file#config-section
  • https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-config

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

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