標籤: 如何寫文案

  • 搭載寶馬發動機!這車還沒上市就引起轟動!

    搭載寶馬發動機!這車還沒上市就引起轟動!

    不僅如此,華晨中華V7採用麥弗遜式獨立前懸、扭力梁式/多連桿式后懸,底盤系統由寶馬和麥格納參与優化,可輕鬆駕馭各種複雜地形和艱險路段,感受隨心而馭、征服天地的操控樂趣。共享寶馬供應商,帶來豪華車的品質體驗。

    4月25日,代表着華晨中華與德國寶馬十五年全產業鏈融合的里程碑之作——中級SUV華晨中華V7在北京車展耀世登場,全球首發。嚴格按照寶馬開發設計流程、搭載BMW授權生產的發動機,與寶馬共享核心供應商體系、按照德系標準生產,顛覆而生的華晨中華V7,旨在以純正德系豪華品質體驗為消費者帶來自主高端SUV新選擇,這也意味着華晨中華與寶馬走向深度融合的全新階段,華晨中華品牌自此邁向“中國寶馬”時代的新起點。

    十五載與巨人同行 華晨中華破繭化蝶

    2003年百年寶馬來到中國,憑藉雄厚的工業基礎和領先的技術優勢,華晨中華成為寶馬在華唯一的合作夥伴,從此開創了中外汽車工業合資合作的傳奇典範。

    攜手十五年,雙方互信共進,華晨中華在寶馬的支持下得到了長足的進步,從造車理念到核心技術、質量控制等,華晨中華始終以德系品質和技術為標準,完成了品質升級和技術沉澱。在品質方面,華晨中華全面引入寶馬品質標準,包括寶馬質量標準體系、寶馬“零缺陷”管理理念、寶馬VpS生產增值系統,在業內首創了產品經理制度和首席質量官制度,建立了貫穿產品質量全過程的ppQ1-10質量保障體系,從生產到研發華晨中華均以德系標準締造高品質自主精品。

    在核心技術方面,華晨中華獲得寶馬授權生產N20及王子系列四款符合歐六排放標準的世界先進發動機,華晨中華因此也成為中國改革開放三十多年來汽車行業唯一一個從合資夥伴取得當代先進核心技術的中方企業。

    掌握了汽車發動機核心技術的華晨中華再次乘勝追擊,在寶馬的支持下,聯合麥格納聯手打造全新M8X智能模塊化平台,該平台在德國工業4.0基礎上構建,並汲取寶馬面向未來的ACES造車戰略理念,具有全面引入寶馬開發流程、原生寶馬技術、寶馬智能製造、智能駕駛等領先優勢。是華晨中華與德國寶馬十五年攜手共進、融匯雙方合作精髓的集大成之作。具備擴展衍生系列化中高端戰略車型的能力。

    M8X智能模塊化平台的到來標志著華晨中華的發展啟動了強勁新引擎,華晨中華正處於品牌與技術厚積薄發的前夜,開創了自主品牌的新時代。

    打造“中國寶馬”華晨中華V7賦能品牌新未來

    作為M8X平台的首款產品,華晨中華V7具備最領先的技術基因、最先進的智能製造水準,最純正的寶馬血統和最高的安全標準,是中國自主品牌中真正具備豪門氣質的划時代傑作。華晨中華V7的推出,對於中華品牌而言不僅是自我的顛覆,更是從動力、品質、安全、智能化四大維度重新定義了自主高端SUV的產品力。

    搭載寶馬源動力,感受純正德系操控樂趣。全新華晨中華V7搭載BMW授權生產的發動機集雙渦管增壓、高精度直噴、連續可變氣門升程、雙連續可變氣門正時等技術於一身,在寶馬專家支持下優化動力總成系統方案設定、匹配,以及對發動機控制系統的重新調校,在格特拉克7速濕式雙離合變速箱的配合下,更將這款發動機的性能發揮到極致,動輸出高達150千瓦,峰值扭矩達280牛·米,百公里加速僅為8.9秒。不僅如此,華晨中華V7採用麥弗遜式獨立前懸、扭力梁式/多連桿式后懸,底盤系統由寶馬和麥格納參与優化,可輕鬆駕馭各種複雜地形和艱險路段,感受隨心而馭、征服天地的操控樂趣。

    共享寶馬供應商,帶來豪華車的品質體驗。隨着華晨中華與德國寶馬合作的深度融合,華晨中華V7所有核心部件共享了寶馬供應商體系:發動機來自BMW授權生產的發動機,變速箱來自格特拉克。此外,更有德國大陸、博世、曼胡默爾、貝爾,美國TRW、德爾福、天納克、江森,法國法雷奧、米其林、意大利馬瑞利,日本海德世、日本三電,韓國岱摩斯、萬都,奧地利MSE等全球零部件領域頂尖品牌組成豪華天團,置身其中,讓德系豪華車尊崇精緻的品質體驗油然而生。

    五星標準設計,暢享安全出行體驗。承襲百年寶馬對安全的執著追求,華晨中華V7按照2018版全新C-NCAp五星標準進行設計,提升對車內外人員全方位主被動保護能力。選用與BMW相同品質的寶鋼板材,高強度鋼比例達到56%,屬同級最高水平。熱成型加強鋼天窗骨架,安全穩定,A、B柱採用超高強熱成型鋼板,抗拉強度超過1500Mpa,事故中能有效減少駕駛艙變形,保護駕乘人員的安全。此外,華晨中華V7配置了同級中先進防碰撞預警+自動緊急剎車系統,以及領先同級的車道偏離預警系統、盲區監測,以陪伴之心呵護安全,讓每一段旅途都心安。

    萬物互聯,跨越時空的智能體驗。移動互聯時代,萬物盡在雲端,一切盡在掌控。華晨中華V7通過車載WIFI,駕乘者能0秒與世界互聯,配備同級別尺寸最大的10.4寸中控屏,搭載BriAir2.0車聯網系統,可實現語音控制、遠程控制、智聯娛樂等功能,為提供用戶極致人性化的智能汽車新體驗。用戶可通過手機App遠程了解車況,實現對發動機、空調、車窗、車鎖等全方位控制,尋車、防盜報警等智能化功能能夠實現即使人車分離也能對車輛狀況了如指掌。車輛與智能手機的“無縫接軌”,讓智能體驗跨越時空、時刻隨行。

    華晨中華V7的到來以德系標準開啟了華晨中華品牌進入“中國寶馬”時代,代表了追求極致性能自主高端SUV新勢力的崛起,為更多用戶帶來純正德系豪華SUV的新體驗。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

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

  • 夏日將至,稍有不慎愛車易罷工 夏季用車注意事項須知!

    夏日將至,稍有不慎愛車易罷工 夏季用車注意事項須知!

    4。高頻使用空調易吸進灰塵,為此需偶爾開啟最大風量擋,將風道內殘留的浮塵吹出,定期清洗空調濾芯。5。夏季車內迅速降溫妙訣:把駕駛位車窗打開,反覆開關副駕駛位車門,能加速空氣流通,迅速降溫,繼而再開空調輔助降溫,降低空調系統負載。

    夏日將至,正當你泰然自若暢享着車內空調時,你可知愛車卻匍匐在高溫地表上被烈日炙烤?即便如此,它還是兢兢業業將乘員從A點送往B點,不知疲倦,也不曾抱怨。

    殊不知,汽車也有自己的“小情緒”,嚴峻工作條件或成汽車“情緒失控”導火索,稍有不慎在高速路拋錨就夠你吃一壺。為此,總結出夏季用車注意事項。

    1. 輪胎是汽車與地面接觸的唯一部件,長時間在高溫環境下運轉對產品本身的品質和狀況考驗甚高,不容小覷。為此,夏季出行前檢測胎壓、輪胎是否存在鼓包等尤為重要。

    其次,換裝高品質輪胎,如美國固特異御乘系列輪胎就能在性能、舒適與經濟性間取得韌性平衡,雪佛蘭、奔馳、奧迪便是其忠實擁躉。

    2. 夏季氣溫高,水和油容易蒸發,要養成常檢查水箱水位、曲軸箱機油刻度、制動總泵內製動液液面高度、蓄電池內電解液密度和液面高度的習慣。此外,油箱蓋、水箱蓋沒蓋嚴也會加速蒸發。

    3. 部分“老司機”甚至自作聰明以自來水代替防凍液。實則不然,冷卻效果不達標會加劇机械硬件使用壽命的衰減程度。專業防凍液既能耐低溫又能耐高溫,一般防凍液沸點高達200℃,建議選用原廠防凍液為愛車防暑降溫。

    4. 高頻使用空調易吸進灰塵,為此需偶爾開啟最大風量擋,將風道內殘留的浮塵吹出,定期清洗空調濾芯。

    5. 夏季車內迅速降溫妙訣:把駕駛位車窗打開,反覆開關副駕駛位車門,能加速空氣流通,迅速降溫,繼而再開空調輔助降溫,降低空調系統負載。

    坊間存在一種聲音:對汽車保養這一概念模糊,只知道時間開長了送往4S店保養就准沒錯。

    但現況卻是多數廠商僅提供次數少得可憐的免費保養服務,部分4S店銷售甚至終日一副輕佻臉容迎人,在售後保養維修環節亟待改進。

    相比之下,上汽通用雪佛蘭誠摯負責的售後服務態度,可謂當前急於求成的勢利市場中的一股清流。

    在實力中級SUV探界者上市1周年之際,雪佛蘭為2017年探界者車主舉辦售後服務體驗日活動。即日起至5月31日,2017年探界者車主親臨煥新升級的雪佛蘭4S店可體驗貼心售後服務,體驗內容包括:E-Service、雙人快速保養、售後新FED、夏季專項檢測、U·CLUB。

    受邀進店的2017探界者老車主不僅能在活動中親身體驗全新升級的雪佛蘭售後服務,還將獲贈雪佛蘭4S店的機濾券一份。

    活動過程當中,雪佛蘭將挑選專屬服務顧問與經過上汽通用培訓體系專業認證的高水平專業技師為進店車主打造一對一的VIp客戶服務。

    當車主駕車進店時,售後大屏上將智能显示識別車牌后的歡迎標語,此刻專屬服務顧問將第一時間前往接待,車主只需交付車輛,安心地在休息區通過“MyChevy” App上的“掌上車間”模塊輕鬆便捷地掌握在修車輛的服務進度信息,整個維保養過程更為可視化。

    遠不止此,車主還將免費享受夏季專項車輛檢測,其內容涵蓋發動機、變速箱、底盤及電氣,通過多達22項的檢測內容為夏天做好準備。

    在紮實推進全新升級的進程中,雪佛蘭已於2017年完成了全國100家經銷商店面的升級改造,並採用全球統一的設計標準,展現與全球同步的品牌形象。

    未來,雪佛蘭將持續以更優質的服務回饋每一位車主,進一步打造雪佛蘭值得信賴的品牌形象。在硬件設施和軟件配置上融入更多年輕、運動、時尚的新元素,全力打造全球品牌、全球標識的新面貌。

    想更了解雪佛蘭金領結服務,點擊入內:動車更懂本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

  • 最高優惠8萬,這款20多萬的SUV牌子超硬,車主卻說…

    最高優惠8萬,這款20多萬的SUV牌子超硬,車主卻說…

    6/7百公里油耗(L):6。5/7。3車主百公里油耗(L):9。08/10。5驅動方式:前置前驅/前置四驅底盤懸挂:前麥弗遜/后多連桿實際體驗(體驗者182cm):前排頭部3指/後排頭部3指/後排腿部1拳。感興趣的朋友可以點擊小程序查看詳細口碑,從口碑中可以看到車主們對奔馳GLA的內外設計可謂相當滿意,也稱它為適合年輕人代步的車型。

    奔馳GLA類似跨界SUV的車身結構極富有衝擊力,動感化的車身設計語言,還能給用戶帶來一些柔美的觀感體驗!

    而比較特別的是,GLA的坐姿並不高,較低的車身重心帶來更充足的操控信心。它的1.6T車型的動力在D擋模式下被調得頗為平順,而在S擋模式下,動力則靈敏不少,動力輸出變得強勁起來!駕駛起來也挺有樂趣的。

    長寬高:4449*1804*1535mm

    軸距:2699mm

    定位:緊湊型SUV

    在形象方面,奔馳GLA輪廓飽滿、線條充滿力量感,更展現運動與時尚,相對低矮的車身運動氣息強烈!

    內飾方面它不僅做工細緻且設計也有不錯的豪華氣質,獨立是中控屏查看便利,值得一提的是它採用個性的多個圓形空調出風口,精緻感得以體現!

    發動機:1.6T/2.0T

    最大馬力(pS):156/184/211

    最大扭矩(Nm):250/300/350

    變速箱:7DCT

    百公里加速(s):8.5/7.6/7

    百公里油耗(L):6.5/7.3

    車主百公里油耗(L):9.08/10.5

    驅動方式:前置前驅/前置四驅

    底盤懸挂:前麥弗遜/后多連桿

    實際體驗(體驗者182cm):前排頭部3指/後排頭部3指/後排腿部1拳。

    感興趣的朋友可以點擊小程序查看詳細口碑,從口碑中可以看到車主們對奔馳GLA的內外設計可謂相當滿意,也稱它為適合年輕人代步的車型!只是對於車燈光源、頭部空間、懸架的軟硬程度有些不滿。

    咱們發現奔馳GLA的優惠幅度相當可觀,在廣州、武漢、上海等地優惠幅度較大,但需要搭配加裝飾、購買精品、店內上保險、貸款等項目。所有實際能優惠多少還是因為各個地區而有所差異,各位在購車前可要注意了。

    奔馳CLA的內外設計頗有特色,內飾大面積銀色裝飾的運用、鍍鉻的出風口加上懸浮的中控大屏都彰顯檔次感!而且操控性也不錯,只是低配車型的配置不算太高,如果價格適宜的話選擇中配車型更加實用!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

  • 這款帥氣SUV僅8.98萬起步,還能免幾千塊稅費,值嗎?

    這款帥氣SUV僅8.98萬起步,還能免幾千塊稅費,值嗎?

    另一款是最大功率為90kW,這款動力強,0-50km/h加速為4。2秒,極速135km/h,但續航里程只有252km。江鈴E400前麥弗遜后扭力梁的懸挂也是小型SUV的標配。后扭力梁懸挂結構緊湊,可以將空間留給乘客。江鈴E400的加速響應不是很快,地板油下去,加速度會慢慢提升,到達頂點的時候又會立刻下降,跟汽油車非常不同。

    4月30日,江鈴E400正式上市,補貼后的售價為8.98萬-10.98萬(全國統一價格)。同天下午,我們有幸試駕這款純電動SUV—江鈴E400。說實話,我原本有點擔心純電動車高重量的特性會拉低整車的駕駛和乘坐感受,但實際體驗下來,純電動車在某些方面反而有所提升。另外,純電動車前期購買免購置稅,免拍牌費,後期超低使用成本。讓直呼,我還買什麼汽油車。在下文就磕叨磕叨為什麼。

    江鈴E400的車身尺寸並不大,屬於小型SUV的範疇。由於小型SUV多數是面向年輕人的產品,所以江鈴E400的外觀也有不錯的設計感,側面雙腰線的組合令整車充滿動感,起伏有致的尾部與車頭互為呼應。整車的外觀顏值算比較高的。

    江鈴E400的內飾簡潔,用料非常豐富,基本上與人體接觸的地方都用軟性材料包裹。全液晶儀錶盤信息豐富,但是設計感略為不足,比較重要的時速,剩餘電量,續航里程不夠顯眼。另一個問題是,把坐姿調高一點,是看不到儀錶盤轉向燈的指示,只能將座椅調低。希望下一個版本能夠改進。7英寸的中控屏採用了安卓系統,未來可以通過網絡升級系統,目前的功能不多,但是導航,音樂,藍牙電話,電台這幾個基本配置還是有的。

    乘坐空間是江鈴E400的優點,1米75的乘客在前排可頭部有1拳以上的空間。同一位乘客在後排可以獲得兩拳以上的腿部空間,一拳以上的頭部空間。另外,後排中央地台全平,後排中間座椅也沒有凸起。江鈴E400可以保證5個成年人獲得不錯的乘坐空間。

    江鈴E400有兩款動力配置,一款是最大功率為50kW的電機,這款動力弱一點,0-50km/h加速小於6秒,極速120km/h,但是續航更長,達到310km。另一款是最大功率為90kW,這款動力強,0-50km/h加速為4.2秒,極速135km/h,但續航里程只有252km。

    江鈴E400前麥弗遜后扭力梁的懸挂也是小型SUV的標配。后扭力梁懸挂結構緊湊,可以將空間留給乘客。

    江鈴E400的加速響應不是很快,地板油下去,加速度會慢慢提升,到達頂點的時候又會立刻下降,跟汽油車非常不同。江鈴E400的坡道輔助邏輯也很有趣,會先溜后一段,然後剎住。江鈴E400沒有怠速這一功能,起步必須踩油門才能走,不支持蠕動。這兩個點是需要加強的地方。

    江鈴E400的駕駛和乘坐舒適性方面確實給了我很大的驚喜。試駕前,我認為過重的車身會讓懸挂很難找到平衡點,太硬,舒適性差,太軟,點頭嚴重。實際上,我之前是想錯了一點。純電動車將電池裝在前後軸之間,所以前軸和后軸的重量布置更加均勻,不會因為車身重了而加重前軸的負荷。所以江鈴E400的舒適性和操控性都得到了很好地保障。後排舒適性比前排稍差。

    江鈴E400的隔音水準一般,而且配置上寫好的隔熱玻璃也不太隔熱,下午被曬得好燙,這有可能是試裝車的緣故。

    慣例附上充電速度。

    總結:江鈴E400在大方面其實是做得很出色的。整車的空間、舒適性、操控性跟燃油車相比不落下風。但是江鈴E400的小細節上有些粗枝大恭弘=叶 恭弘,所幸的是,這些點可以通過後期軟件的升級來完善。江鈴E400的續航里程可以滿足市區代步,周末短途旅行的需求。個人認為,如果你99%的情況都是在城市生活,一款純電動車會比汽油車來得更加省心省錢。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

  • 同是2.0T+7速雙離合,為何那麼多人放棄大眾買貴4萬的豪車?

    同是2.0T+7速雙離合,為何那麼多人放棄大眾買貴4萬的豪車?

    我曾經從一位一汽的裝配工口中聽過這樣一句話:大眾的車底盤焊縫要求不能伸進指甲就合格,但是。奧迪的要求是不能伸進頭髮。上面我們說到了兩車的造車成本以及測試種類,那就不免出現一個更加專業名詞——NVH,直白一點說就是車輛的胎噪,風噪和發動機噪音。

    在如今這個車水馬龍的城市中,滿大街都是車,而車一多,咱們買車選車就自然變得更加困難了。而且中國人買車需要的可不單單隻是性價比,更需要的是面子,並且一定要大。

    特別是當BBA這種豪華品牌不斷的將車型價格下探之後,吸引了更多的中國人可以花普通品牌的價錢買到掛着“豪華品牌商標”的汽車,這也是為什麼如今奔馳、寶馬、奧迪這些車會滿大街跑的原因。

    當然,相比起豪華品牌的紙醉金迷,普通品牌的優勢更多在於極高性價比。不過不可否認的是,豪華品牌的品牌溢價能力,的確是普通品牌無法比擬的。

    並且豪華車除了可以給人帶來身份的象徵,還帶來了更極佳的駕乘感受,畢竟“豪華”兩字已經說明了一切。如今各類豪華品牌的入門車型售價,儼然已經可以跟普通品牌針鋒相對了。

    那在面對預算有限的情況下,到底是咬咬牙弄台豪華品牌,享受一下豪車帶來的優越感呢,還是輕鬆的拿下普通品牌,得到其極具性價比配置的同時還能多買幾台蘋果XXX呢?

    如果有一天,你開着一輛頂配大眾高爾夫GTI去相親,對方父母只會認為你混得一般,但是當你開着一輛掛這“四個環”的丐版奧迪A3去相親,人家則會認為,這家孩子不錯,年紀輕輕就開得起奧迪了。

    一輛頂配的GTI比起低配的奧迪A3,怎麼著也得貴上好幾萬吧,但是為什麼人人都會認為奧迪更高級呢。

    奧迪嘛,在國人心目中一直存在着“官車”的形象,而大眾則是普羅大眾都消費得起的存在。而且再怎麼說你也只是大眾,我可是奧迪啊,世界公認的三大豪華品牌之一,買車看的不正是牌子,面子嘛!

    不過很多朋友都認為,A3其實就是換標高爾夫,而且那個標還不便宜。那這次咱們就以高爾夫GTI以及奧迪A3兩廂頂配來說話。到底用幾台蘋果XXX的錢去買一個四環標值不值得?

    目前兩車在廣州的裸車價基本是:奧迪A3頂配23萬左右,高爾夫GTI19萬左右,也就是四萬左右的差價,雖然不同地區情況不同,不過差價相信基本也是相差無幾。

    相信有了解過這兩款車的朋友都知道,兩者同為一汽旗下合資公司的產品、同是MQB平台、同一工廠生產、相同的EA888發動機、相同的變速箱。聽起來兩者的差異性極小,但是細分之後你就知道還是有區別的。

    相同的EA888發動機,在奧迪A3頂配身上只能輸出190匹馬力,但是在GTI身上則能爆發出220馬力。兩者都匹配了7速雙離合變速器,不過在大眾身上叫DSG,在奧迪身上喊S tronic而已。

    從平台上看,兩者均出於MQB橫置模塊化平台,這就不難讓人認為兩者的底盤變現以及操控性上是不是一致呢?事實卻並非如此。就好比一台計算機,平台就如同一個主板,再裝配不同的顯卡,聲卡,內存,CpU,不同搭配所得出的效果自然也是截然不同的。

    另外,雖然奧迪有着品牌溢價,但相對應的供應商也會與大眾有所區別,零配件的成本也會相應的提高,並且在裝配工藝上奧迪也高於大眾。。單單從用漆方面就可以看得出這點,奧迪A3大部分採用的均為高成本、高品質的珠光漆。

    還有奧迪需要做的測試種類,無論是整車測試還是零部件試驗的嚴苛程度,複雜性,精密度,都遠遠高於大眾。我曾經從一位一汽的裝配工口中聽過這樣一句話:大眾的車底盤焊縫要求不能伸進指甲就合格,但是………奧迪的要求是不能伸進頭髮!!!

    上面我們說到了兩車的造車成本以及測試種類,那就不免出現一個更加專業名詞——NVH,直白一點說就是車輛的胎噪,風噪和發動機噪音。從各大測評機構的測試結果都不難看出,奧迪A3對NVH的把控要比高爾夫好,另一方面也表明了,豪華品牌在駕乘感受上下的功夫要比大眾這種普通品牌更多。

    至於外觀,咱們就略過不說了,反正蘿蔔青菜各有所愛,奧迪自帶四環光環,高爾夫GTI則滿身情懷。內飾的話,GTI也不能說完敗,只能說那萬年不變的布局真的讓人又愛又恨,而A3的內飾那才真的稱得上人人喜愛,既精緻又簡潔還顯高級感,而且細看內飾的做工用料,無論是視覺的衝擊力還是觸感的細膩度上,相信什麼叫奧迪,什麼叫大眾自然一目瞭然了。

    不過在配置的差異上,奧迪A3比起GTI似乎真的閹割太多了,要知道那可是四萬塊的差價啊。此外,奧迪選裝配置的價錢也非常感人,一套真皮座椅就小一萬了,估計想選裝到GTI這麼豐富配置的時候你就得多掏幾百張毛爺爺了,不得不說奧迪這生意還真好賺!

    估計看到這裏,大家都不難得出一個結論:奧迪A3當然不是換標高爾夫那麼簡單。

    儘管GTI的馬力、配置都比A3更優越,但是從用料做工、質感品控、駕乘感受方面來看,A3比起GTI則是更勝一籌。終究是豪華品牌嘛,還是會有自己的擔當,哪怕是十幾萬起步的入門車型,他們都會用心去對待。所以與其說多花了幾萬塊錢要裝逼買個豪華商標,倒不如老老實實的承認“一分錢一分貨”這個道理。

    再舉個例子,如果奧迪A3賣着高爾夫的價錢,我估計你也不大敢買吧?又或者說,奧迪如果把A3的價格降低到高爾夫這個層次,我相信一定會大大衝擊高爾夫的銷量,而且奧迪作為豪華品牌的形象也會因此大打折扣。在汽車界,類似的例子比比皆是,所以單單拿平台來說事,似乎就有點無稽之談了。

    至於遇到類似的問題該怎麼去選擇,那就只能看個人需求以及兜里毛爺爺的多與少啦。反正有錢的話,那必須選豪華品牌,去酒吧把鑰匙往桌子一拍,嘿嘿,那效應老司機都懂都懂!本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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

  • 玩車的人都在捧的本田發動機,真的這麼神?

    玩車的人都在捧的本田發動機,真的這麼神?

    然而,這種熟悉的“爆tec”感覺沒有出現在最新的謳歌TLX-L上。謳歌是本田的豪華品牌,一切的用料,技術,做工等都用上高標準。就連本田被人詬病的隔音謳歌也能做得安靜。被拉高轉的本田發動機“爆tec”時的感覺聲音和動力都有着明顯變化,讓人為之興奮,但開着謳歌TLX-L卻沒有這種感覺。

    本田,一個無人不知無人不曉的一個汽車品牌。尤其是在廣東地區,更是本田粉活躍的地方,大街上跑的10輛車中,有4輛或5輛是本田車型。

    買本田車,除了買質量可靠性外,更多的是為了“買發動機送車”,而坊間也流傳着本田金句VTEC is the best。可想而知,本田發動機技術含量和民望有多高。

    本田發動機的看家技術VTEC和i-VTEC技術外,在近年也推出VTEC Turbo這渦輪組合技術。

    那本田技術牛在哪?主要除了有可變氣門正時外,還有氣門升程這一技術。氣門升程技術並不是很多發動機有,除了寶馬和英菲尼迪VQ37系列外,還真是寥寥無幾。

    開過本田的人未必知道什麼叫“爆tec”,但拉過高轉的人就知道。當發動機轉速拉到5000轉左右的時候,聲音突變,發動機也感覺顫抖一下。這就是“爆tec”給人的感覺。

    然而,這種熟悉的“爆tec”感覺沒有出現在最新的謳歌TLX-L上。謳歌是本田的豪華品牌,一切的用料,技術,做工等都用上高標準。就連本田被人詬病的隔音謳歌也能做得安靜。

    被拉高轉的本田發動機“爆tec”時的感覺聲音和動力都有着明顯變化,讓人為之興奮,但開着謳歌TLX-L卻沒有這種感覺。難道就因為高端車型而被“技術”隔絕?

    其實這種“爆tec”的感覺在自主品牌的渦輪車上有着“更好的體現”。只不過我們吐槽它而已,這一技術就是渦輪遲滯。體驗過大部分自主品牌渦輪車,輕踩油門不去,踩重一點像是被踢出去的感覺。這種感覺比“爆tec”還爽快。

    總結:高轉“爆tec”還是低轉渦輪介入明顯,都使得發動機動力輸出沒那麼“線性”。只是拉高轉的時候,亢奮的聲音和動力的凸顯使得“爆tec”的感覺被神化。渦輪介入明顯,其實就是“爆tec”時動力凸顯的一部分,只是沒有亢奮的聲音罷了。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

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

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

  • Python機器學習筆記:SVM(4)——sklearn實現,Python機器學習筆記:SVM(1)——SVM概述,Python機器學習筆記:SVM(2)——SVM核函數,Python機器學習筆記:SVM(3)——證明SVM,Python機器學習筆記:SVM(4)——sklearn實現

    Python機器學習筆記:SVM(4)——sklearn實現,Python機器學習筆記:SVM(1)——SVM概述,Python機器學習筆記:SVM(2)——SVM核函數,Python機器學習筆記:SVM(3)——證明SVM,Python機器學習筆記:SVM(4)——sklearn實現

      上一節我學習了SVM的推導過程,下面學習如何實現SVM,具體的參考鏈接都在第一篇文章中,SVM四篇筆記鏈接為:

    Python機器學習筆記:SVM(1)——SVM概述

    Python機器學習筆記:SVM(2)——SVM核函數

    Python機器學習筆記:SVM(3)——證明SVM

    Python機器學習筆記:SVM(4)——sklearn實現

      對SVM的概念理清楚后,下面我們對其使用sklearn進行實現。

    1,Sklearn支持向量機庫概述

      我們知道SVM相對感知器而言,它可以解決線性不可分的問題,那麼它是如何解決的呢?其思想很簡單就是對原始數據的維度變換,一般是擴維變換,使得原樣本空間中的樣本點線性不可分,但是在變維之後的空間中樣本點是線性可分的,然後再變換后的高維空間中進行分類。

      上面將SVM再贅述了一下,下面學習sklearn中的SVM方法,sklearn中SVM的算法庫分為兩類,一類是分類的算法庫,主要包含LinearSVC,NuSVC和SVC三個類,另一類是回歸算法庫,包含SVR,NuSVR和LinearSVR三個類,相關模塊都包裹在sklearn.svm模塊中。

      對於SVC,NuSVC和LinearSVC 三個分類的庫,SVC和NuSVC差不多,區別僅僅在於對損失的度量方式不同,而LinearSVC從名字就可以看出,他是線性分類,也就是不支持各種低維到高維的核函數,僅僅支持線性核函數,對線性不可分的數據不能使用。

      同樣的對於SVR,NuSVR和LinearSVR 三個回歸的類,SVR和NuSVR差不多,區別也僅僅在於對損失的度量方式不同。LinearSVR是線性回歸,只能使用線性核函數。

      我們使用這些類的時候,如果有經驗知道數據是線性可以擬合的,那麼使用LinearSVC去分類或者LinearSVR去回歸,他們不需要我們去慢慢的調參選擇各種核函數以及對應的參數,速度也快。如果我們對數據分佈沒有什麼經驗,一般使用SVC去分類或者SVR去回歸,這就需要我們選擇核函數以及對核函數調參了。

    2,回顧SVM分類算法和回歸算法

      我們這裏仍然先對SVM算法進行回顧,首先對於SVM分類算法,其原始形式如下:

      其中 n 為樣本個數,我們的樣本為(x1,  y1),(x2,y2),….(xn,  yn),w,b是我們的分離超平面的 wT*xi + b = 0的係數,ξi 為第 i 個樣本的鬆弛係數,C 為懲罰係數,xi (也有時候寫為Φ(xi) 為低維到高維的映射函數)為樣本數。

      通過拉格朗日以及對偶化后的形式為:

      其中和原始形式不同的 α 為拉格朗日係數向量,<xi,  xj> 為我們要使用的核函數。

      對於SVM回歸算法,(我自己沒有總結,借用劉建平老師的博客),其原始形式如下:

      其中 m 為樣本個數,我們的樣本為(x1, y1),(x2,  y2),….,(xm, ym),w,b是我們回歸超平面 wT*xi + b = 0 的係數,ξv, ξ^ 為第 i 個樣本的鬆弛係數, C為懲罰係數,ε 為損失邊界,到超平面距離小於 ε 的訓練集的點沒有損失,Φ(xi) 為低維到高維的映射函數

      通過拉格朗日函數以及對偶后的形式為:

      其中和原始形式不同的 αv, α^ 為拉格朗日係數向量,K(xi, xj) 為我們要使用的核函數。

    3,SVM核函數概述

      我在第二篇SVM中學習了核函數,有好幾種,最常用的就是線性核函數,多項式核函數,高斯核函數和Sigmoid核函數,在scikit-learn中,內置的核函數也剛好有這四種。

    3.1,線性核函數(Linear Kernel)

      線性核函數表達式為:

      就是普通的內積,LinearSVC和LinearSVR只能使用它。

    3.2,多項式核函數(Polynomial Kernel)

      多項式核函數是線性不可分SVM常用的核函數之一,表達式為:

      參數都需要自己調參定義,比較麻煩。

    3.3,高斯核函數(Gaussian Kernel)

      高斯核函數,在SVM中也稱為 徑向基核函數(Radial Basisi Function,RBF),它是libsvm默認的核函數,當然也是sklearn默認的核函數,表達式為:

      其中 r 大於0,需要自己調參定義,不過一般情況,我們都使用高斯核函數。

    3.4,Sigmoid核函數(Sigmoid Kernel)

      Sigmoid核函數也是線性不可分SVM常用的核函數之一,表示為:

      其中 beta, t 都需要自己調參定義。

      一般情況下,對於非線性數據使用默認的高斯核函數會有比較好的效果,如果你不是SVM調參高手的話,建議使用高斯核來做數據分析。

    4,SVM分類算法庫參數小結

      下面我們將具體介紹這三種分類方法都有那些參數值以及不同參數值的含義。

    4.1, LinearSVC

      其函數原型如下:

    class sklearn.svm.LinearSVC(self, penalty='l2', loss='squared_hinge', dual=True, tol=1e-4,
                 C=1.0, multi_class='ovr', fit_intercept=True,
                 intercept_scaling=1, class_weight=None, verbose=0,
                 random_state=None, max_iter=1000)
    

      參數說明

    • penalty :正則化參數,L1 和L2兩種參數可選,僅LinearSVC有。默認是L2 正則化,如果我們需要產生稀疏的話,可以選擇L1正則化,這和線性回歸裏面的Lasso回歸類似
    • loss:損失函數,有“hinge” 和“squared_hinge” 兩種可選,前者又稱為L1損失,後者稱為L2損失,默認是“squared_hinge”,其中hinge是SVM的標準損失,squared_hinge是hinge的平方。
    • dual:是否轉化為對偶問題求解,默認是True。這是一個布爾變量,控制是否使用對偶形式來優化算法。
    • tol:殘差收斂條件,默認是0.0001,與LR中的一致。
    • C:懲罰係數,用來控制損失函數的懲罰係數,類似於LR中的正則化係數。默認為1,一般需要通過交叉驗證來選擇一個合適的C,一般來說,噪點比較多的時候,C需要小一些
    • multi_class:負責多分類問題中分類策略制定,有‘ovr’和‘crammer_singer’ 兩種參數值可選,默認值是’ovr’,’ovr’的分類原則是將待分類中的某一類當作正類,其他全部歸為負類,通過這樣求取得到每個類別作為正類時的正確率,取正確率最高的那個類別為正類;‘crammer_singer’ 是直接針對目標函數設置多個參數值,最後進行優化,得到不同類別的參數值大小。
    •  fit_intercept:是否計算截距,與LR模型中的意思一致。
    • class_weight:與其他模型中參數含義一樣,也是用來處理不平衡樣本數據的,可以直接以字典的形式指定不同類別的權重,也可以使用balanced參數值。如果使用“balanced”,則算法會自己計算權重,樣本量少的類別所對應的樣本權重會高,當然,如果你的樣本類別分佈沒有明顯的偏倚,則可以不管這個係數,選擇默認的None
    • verbose:是否冗餘,默認為False
    • random_state:隨機種子的大小
    • max_iter:最大迭代次數,默認為1000.

    懲罰係數:

      錯誤項的懲罰係數。C越大,即對分錯樣本的懲罰程度越大,因此在訓練樣本中準確率越高,但是泛化能力降低,也就是對測試數據的分類準確率降低。相反,減少C的話,容許訓練樣本中有一些誤分類錯誤樣本,泛化能力強。對於訓練樣本帶有噪音的情況,一般採用後者,把訓練樣本集中錯誤分類的樣本作為噪音。

    4.2,NuSVC

      其函數原型如下:

    class sklearn.svm.NuSVC(self, nu=0.5, kernel='rbf', degree=3, gamma='auto_deprecated',
                 coef0=0.0, shrinking=True, probability=False, tol=1e-3,
                 cache_size=200, class_weight=None, verbose=False, max_iter=-1,
                 decision_function_shape='ovr', random_state=None)
    

       參數說明

    • nu:訓練誤差部分的上限和支持向量部分的下限,取值在(0,1)之間,默認是0.5,它和懲罰係數C類似,都可以控制懲罰的力度。
    • kernel:核函數,核函數是用來將非線性問題轉化為線性問題的一種方法,默認是“rbf”核函數

          常用的核函數有以下幾種:

    • degree:當核函數是多項式核函數(“poly”)的時候,用來控制函數的最高次數。(多項式核函數是將低維的輸入空間映射到高維的特徵空間),這個參數只對多項式核函數有用,是指多項式核函數的階數 n。如果給的核函數參數是其他核函數,則會自動忽略該參數。
    • gamma:核函數係數,默認是“auto”,即特徵維度的倒數。核函數係數,只對rbf  poly  sigmoid 有效。
    • coef0:核函數常數值( y = kx + b 的b值),只有“poly”和“sigmoid” 函數有,默認值是0.
    • max_iter:最大迭代次數,默認值是 -1 ,即沒有限制。
    • probability:是否使用概率估計,默認是False。
    • decision_function_shape:與“multi_class”參數含義類似,可以選擇“ovo” 或者“ovr”(0.18版本默認是“ovo”,0.19版本為“ovr”) OvR(one vs rest)的思想很簡單,無論你是多少元分類,我們都可以看做二元分類,具體的做法是,對於第K類的分類決策,我們把所有第K類的樣本作為正例,除第K類樣本以外的所有樣本作為負類,然後在上面做二元分類,得到第K類的分類模型。 OvO(one vs one)則是每次在所有的T類樣本裏面選擇兩類樣本出來,不妨記為T1類和T2類,把所有的輸出為T1 和 T2的樣本放在一起,把T1作為正例,T2 作為負例,進行二元分類,得到模型參數,我們一共需要T(T-1)/2 次分類。從上面描述可以看出,OvR相對簡單,但是分類效果略差(這裡是指大多數樣本分佈情況,某些樣本分佈下OvR可能更好),而OvO分類相對精確,但是分類速度沒有OvR快,一般建議使用OvO以達到較好的分類效果
    • chache_size:緩衝大小,用來限制計算量大小,默認是200M,如果機器內存大,推薦使用500MB甚至1000MB

    4.3,SVC

      其函數原型如下:

    class sklearn.svm.SVC(self, C=1.0, kernel='rbf', degree=3, gamma='auto_deprecated',
                 coef0=0.0, shrinking=True, probability=False,
                 tol=1e-3, cache_size=200, class_weight=None,
                 verbose=False, max_iter=-1, decision_function_shape='ovr',
                 random_state=None)
    

      參數說明:

    • C:懲罰係數(前面有詳細學習)

      SVC和NuSVC方法基本一致,唯一區別就是損失函數的度量方式不同(NuSVC中的nu參數和SVC中的C參數)即SVC使用懲罰係數C來控制懲罰力度,而NuSVC使用nu來控制懲罰力度。

    5,SVM回歸算法庫參數小結

      下面我們將具體介紹這三種分類方法都有那些參數值以及不同參數值的含義。

    5.1, LinearSVR

      其函數原型如下:

    class sklearn.svm.LinearSVR(self, epsilon=0.0, tol=1e-4, C=1.0,
                 loss='epsilon_insensitive', fit_intercept=True,
                 intercept_scaling=1., dual=True, verbose=0,
                 random_state=None, max_iter=1000)
    

      參數說明

    • epsilon:距離誤差epsilon,即回歸模型中的 epsilon,訓練集中的樣本需要滿足:
    • loss:損失函數,有“hinge” 和“squared_hinge” 兩種可選,前者又稱為L1損失,後者稱為L2損失,默認是“squared_hinge”,其中hinge是SVM的標準損失,squared_hinge是hinge的平方。
    • dual:是否轉化為對偶問題求解,默認是True。這是一個布爾變量,控制是否使用對偶形式來優化算法。
    • tol:殘差收斂條件,默認是0.0001,與LR中的一致。
    • C:懲罰係數,用來控制損失函數的懲罰係數,類似於LR中的正則化係數。默認為1,一般需要通過交叉驗證來選擇一個合適的C,一般來說,噪點比較多的時候,C需要小一些
    •  fit_intercept:是否計算截距,與LR模型中的意思一致。
    • verbose:是否冗餘,默認為False
    • random_state:隨機種子的大小
    • max_iter:最大迭代次數,默認為1000.

    5.2,NuSVR

      其函數原型如下:

    class sklearn.svm.NuSVR(self, nu=0.5, C=1.0, kernel='rbf', degree=3,
                 gamma='auto_deprecated', coef0=0.0, shrinking=True,
                 tol=1e-3, cache_size=200, verbose=False, max_iter=-1)
    

       參數說明

    • nu:訓練誤差部分的上限和支持向量部分的下限,取值在(0,1)之間,默認是0.5,它和懲罰係數C類似,都可以控制懲罰的力度。
    • kernel:核函數,核函數是用來將非線性問題轉化為線性問題的一種方法,默認是“rbf”核函數

          常用的核函數有以下幾種:

    • degree:當核函數是多項式核函數(“poly”)的時候,用來控制函數的最高次數。(多項式核函數是將低維的輸入空間映射到高維的特徵空間),這個參數只對多項式核函數有用,是指多項式核函數的階數 n。如果給的核函數參數是其他核函數,則會自動忽略該參數。
    • gamma:核函數係數,默認是“auto”,即特徵維度的倒數。核函數係數,只對rbf  poly  sigmoid 有效。
    • coef0:核函數常數值( y = kx + b 的b值),只有“poly”和“sigmoid” 函數有,默認值是0.
    • chache_size:緩衝大小,用來限制計算量大小,默認是200M,如果機器內存大,推薦使用500MB甚至1000MB

    5.3,SVR

      其函數原型如下:

    class sklearn.svm.SVC(self, kernel='rbf', degree=3, gamma='auto_deprecated',
                 coef0=0.0, tol=1e-3, C=1.0, epsilon=0.1, shrinking=True,
                 cache_size=200, verbose=False, max_iter=-1)
    

      參數說明:

      SVR和NuSVR方法基本一致,唯一區別就是損失函數的度量方式不同(NuSVR中的nu參數和SVR中的C參數)即SVR使用懲罰係數C來控制懲罰力度,而NuSVR使用nu來控制懲罰力度。

    6,SVM的方法與對象

    6.1 方法

      三種分類的方法基本一致,所以一起來說:

    • decision_function(x):獲取數據集X到分離超平面的距離
    • fit(x , y):在數據集(X,y)上使用SVM模型
    • get_params([deep]):獲取模型的參數
    • predict(X):預測數值型X的標籤
    • score(X,y):返回給定測試集合對應標籤的平均準確率

    6.2  對象

    • support_:以數組的形式返回支持向量的索引
    • support_vectors_:返回支持向量
    • n_support_:每個類別支持向量的個數
    • dual_coef:支持向量係數
    • coef_:每個特徵係數(重要性),只有核函數是LinearSVC的是可用,叫權重參數,即w
    • intercept_:截距值(常數值),稱為偏置參數,即b

      加粗的三個屬性是我們常用的,後面會舉例說明 support_vectors_。

     

    7,SVM類型算法的模型選擇

    7.1 PPT總結

      這裏使用(http://staff.ustc.edu.cn/~ketang/PPT/PRLec5.pdf)的PPT進行整理。

    7.2 SVM算法庫其他調參要點

      下面再對其他調參要點做一個小結:

    • 1,一般推薦在做訓練之前對數據進行歸一化,當然測試集的數據也要做歸一化
    • 2,在特徵數非常多的情況下,或者樣本數遠小於特徵數的時候,使用線性核,效果就很好了,並且只需要選擇懲罰係數C即可
    • 3,在選擇核函數的時候,如果線性擬合效果不好,一般推薦使用默認的高斯核(rbf),這時候我們主要對懲罰係數C和核函數參數 gamma 進行調參,經過多輪的交叉驗證選擇合適的懲罰係數C和核函數參數gamma。
    • 4,理論上高斯核不會比線性核差,但是這個理論就建立在要花費更多的時間上調參上,所以實際上能用線性核解決的問題我們盡量使用線性核函數

       在SVM中,其中最重要的就是核函數的選取和參數選擇了,當然這個需要大量的經驗來支撐,這裏幾個例子只是自己網上找的SVM的小例子。

    8,SVM調參實例1

      下面學習支持向量機的使用方法以及一些參數的調整,支持向量機的原理就是將低維不可分問題轉換為高維可分問題。這裏不再贅述。

    8.1  線性可分支持向量機

      首先做一個簡單的線性可分的例子,這裏直接使用sklearn.datasets.make_blobs 生成數據。生成數據代碼如下:

    # 生成數據集
    from sklearn.datasets.samples_generator import make_blobs
    from matplotlib import pyplot as plt
    
    # n_samples=50 表示取50個點,centers=2表示將數據分為兩類
    X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.6)
    # 畫圖形
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    plt.show()
    

       我們畫圖展示如下:

      我們嘗試繪製分離兩組數據的直線,從而創建分類模型,對於這裏所示的數據,這是我們可以手動完成的任務。但是立馬可以看出有很多分界線可以完美的區分兩個類。

       下面畫出決策邊界。

    # 生成數據集
    from sklearn.datasets.samples_generator import make_blobs
    from matplotlib import pyplot as plt
    import numpy as np
    
    # n_samples=50 表示取50個點,centers=2表示將數據分為兩類
    X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.6)
    
    # 畫圖形
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    # 線性等分詳細
    xfit = np.linspace(-1, 3.5)
    plt.plot([0.6], [2.1], 'x', color='red', markeredgewidth=2, markersize=10)
    
    for m, b in [(1, 0.65), (0.5, 1.6), (-0.2, 2.9)]:
        plt.plot(xfit, m * xfit + b, '-k')
    
    plt.show()
    

       圖如下:

      (注意:這三條直線是我隨便畫的,其實你可以使用Logistic回歸,線性回歸等分類,畫出線,我這裡是為了方便)

       這裡是三條不同的分割直線,並且這些分割直線能夠完全區分這些樣例。但是根據支持向量機的思想,哪一條直線是最優的分割線呢?支持向量機並不是簡單的繪製一條直線,而是畫出邊距為一定寬度的直線,直到最近的點。

      下面我們對直線進行加粗,代碼如下:

    # 生成數據集
    from sklearn.datasets.samples_generator import make_blobs
    from matplotlib import pyplot as plt
    import numpy as np
    
    # n_samples=50 表示取50個點,centers=2表示將數據分為兩類
    X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.6)
    
    # 畫圖形
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    # 線性等分詳細
    xfit = np.linspace(-1, 3.5)
    plt.plot([0.6], [2.1], 'x', color='red', markeredgewidth=2, markersize=10)
    
    for m, b, d in [(1, 0.65, 0.33), (0.5, 1.6, 0.55), (-0.2, 2.9, 0.2)]:
        yfit = m * xfit + b
        plt.plot(xfit, yfit, '-k')
        plt.fill_between(xfit, yfit - d, yfit + d, edgecolor='none',
                         color='#AAAAAA', alpha=0.4)  # alpha為透明度
    plt.show()
    

       如圖所示:

       在支持向量機中,邊距最大化的直線是我們將選擇的最優模型。支持向量機是這種最大邊距估計器的一個例子。

      接下來,我們訓練一個基本的SVM,我們使用sklearn的支持向量機,對這些數據訓練SVM模型。目前我們將使用一個線性核並將C參數設置為一個默認的數值。如下:

    from sklearn.svm import SVC  # Support Vector Classifier
    
    model = SVC(kernel='linear') # 線性核函數
    model.fit(X, y)
    

       我們順便看看SVC的所有參數情況:

    SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
        decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
        kernel='linear', max_iter=-1, probability=False, random_state=None,
        shrinking=True, tol=0.001, verbose=False)
    

       為了更好展現這裏發生的事情,下面我們創建一個輔助函數,為我們繪製SVM的決策邊界。

    def plot_SVC_decision_function(model, ax=None, plot_support=True):
        '''Plot the decision function for a 2D SVC'''
        if ax is None:
            ax = plt.gca()  #get子圖
        xlim = ax.get_xlim()
        ylim = ax.get_ylim()
    
        # create grid to evaluate model
        x = np.linspace(xlim[0], xlim[1], 30)
        y = np.linspace(ylim[0], ylim[1], 30)
        # 生成網格點和坐標矩陣
        Y, X = np.meshgrid(y, x)
        # 堆疊數組
        xy = np.vstack([X.ravel(), Y.ravel()]).T
        P = model.decision_function(xy).reshape(X.shape)
    
        # plot decision boundary and margins
        ax.contour(X, Y, P, colors='k', levels=[-1, 0, 1],
                   alpha=0.5, linestyles=['--', '-', '--'])  # 生成等高線 --
    
        # plot support vectors
        if plot_support:
            ax.scatter(model.support_vectors_[:, 0],
                       model.support_vectors_[:, 1],
                       s=300, linewidth=1, facecolors='none')
    
        ax.set_xlim(xlim)
        ax.set_ylim(ylim)
    

       下面繪製決策邊界:

    def train_SVM():
        # n_samples=50 表示取50個點,centers=2表示將數據分為兩類
        X, y = make_blobs(n_samples=50, centers=2, random_state=0, cluster_std=0.6)
    
        # 線性核函數
        model = SVC(kernel='linear')
        model.fit(X, y)
        plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
        plot_SVC_decision_function(model)
        plt.show()
        return X, y
    

       結果如圖所示:

       這是最大化兩組點之間的間距的分界線,那中間這條線就是我們最終的決策邊界了。請注意:一些訓練點碰到了邊緣,如圖所示,在兩個邊界上包含兩個紅點和一個黃點,所以這三個點又稱為支持向量,是 alpha 值不為零的,這些點是這種擬合的關鍵要素,被稱為支持向量。在sklearn中,這些點存儲在分類器的 support_vectors_ 屬性中。

      我們通過下面代碼可以得出支持向量的結果。

        print(model.support_vectors_)
        '''
        [[0.44359863 3.11530945]
         [2.33812285 3.43116792]
         [2.06156753 1.96918596]]
        '''
    

      在支持向量機只有位於支持向量上面的點才會對決策邊界有影響,也就是說不管有多少的點是非支持向量,那對最終的決策邊界都不會產生任何影響。我們可以看到這一點,例如,如果我們繪製該數據集的前 60個點和前120個點獲得的模型:

    def plot_svm(N=10, ax=None):
        X, y = make_blobs(n_samples=200, centers=2, random_state=0, cluster_std=0.6)
        X, y = X[:N], y[:N]
        model = SVC(kernel='linear')
        model.fit(X, y)
    
        ax = ax or plt.gca()
        ax.scatter(X[:, 0], X[:, 1], c=y, cmap='autumn')
        ax.set_xlim(-1, 4)
        ax.set_ylim(-1, 6)
        plot_SVC_decision_function(model, ax)
    
    if __name__ == '__main__':
        # train_SVM()
        fig, ax = plt.subplots(1, 2, figsize=(16, 6))
        fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
        for axi, N in zip(ax, [60, 120]):
            plot_svm(N, axi)
            axi.set_title('N = {0}'.format(N))
    

       結果如圖所示:

      上面就是我們繪製的該數據集前60個點和前120個點獲得的模型,可以發現無論使用60,還是使用120個數據點,決策邊界都沒有發生變換,所有隻要支持向量沒變,其他的數據怎麼加都無所謂。

       這個分類器成功的關鍵在於:為了擬合,只有支持向量的位置是最重要的;任何遠離邊距的點都不會影響擬合的結果,邊界之外的點無論有多少都不會對其造成影響,也就是說不管有多少點是非支持向量,對最終的決策邊界都不會產生任何影響。

    8.2 線性不可分支持向量機

      下面引入核函數,來看看核函數的威力,首先我們導入一個線性不可分的數據集。

    def train_svm_plus():
        # 二維圓形數據 factor 內外圓比例(0, 1)
        X, y = make_circles(100, factor=0.1, noise=0.1)
        
        clf = SVC(kernel='linear')
        clf.fit(X, y)
        
        plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
        plot_SVC_decision_function(clf, plot_support=False)
    

       數據集如圖所示:

       很明顯,用線性分類器無論怎麼畫線也不能分好,那咋辦呢?下面試試高斯核變換吧。在進行核變換之前,先看看數據在高維空間下的映射:

    def plot_3D(X, y, elev=30, azim=30):
        # 我們加入了新的維度 r
        r = np.exp(-(X ** 2).sum(1))
        ax = plt.subplot(projection='3d')
        ax.scatter3D(X[:, 0], X[:, 1], r, c=y, s=50, cmap='autumn')
        ax.view_init(elev=elev, azim=azim)
        ax.set_xlabel('x')
        ax.set_ylabel('y')
        ax.set_zlabel('z')
    
    
    if __name__ == '__main__':
        X, y = train_svm_plus()
        plot_3D(elev=30, azim=30, X=X, y=y)
    

       畫出三維圖形,如圖所示:

       見證核變換威力的時候到了,引入徑向基函數(也叫高斯核函數),進行核變換:

    def train_svm_plus():
        # 二維圓形數據 factor 內外圓比例(0, 1)
        X, y = make_circles(100, factor=0.1, noise=0.1)
        # 加入徑向基函數
        clf = SVC(kernel='rbf')
        clf.fit(X, y)
    
        plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
        plot_SVC_decision_function(clf, plot_support=False)
        return X, y
    

       得到的SVM模型為:

    SVC(C=1000000.0, cache_size=200, class_weight=None, coef0=0.0,
        decision_function_shape='ovr', degree=3, gamma='auto_deprecated',
        kernel='rbf', max_iter=-1, probability=False, random_state=None,
        shrinking=True, tol=0.001, verbose=False)
    

       再次進行分類任務,代碼如下:

    def train_svm_plus():
        # 二維圓形數據 factor 內外圓比例(0, 1)
        X, y = make_circles(100, factor=0.1, noise=0.1)
        # 加入徑向基函數
        clf = SVC(kernel='rbf')
        clf.fit(X, y)
    
        plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
        plot_SVC_decision_function(clf, plot_support=False)
        plt.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
                    s=300, lw=1, facecolors='none')
        return X, y
    

       分類結果如圖所示:

       可以清楚的看到效果很好,我們將線性不可分的兩對數據分割開來。使用這種核支持向量機,我們學習一個合適的非線性決策邊界。這種核變換策略在機器學習中經常被使用。

    8.3 線性近似可分支持向量機——軟間隔問題

      SVM模型有兩個非常重要的參數C與gamma,其中C是懲罰係數,即對誤差的寬容忍,C越高,說明越不能容忍出現誤差,容易過擬合。C越小,容易欠擬合。C過大或過小,泛化能力變差。

      gamma 是選擇 RBF 函數作為kernel后,該函數自帶的一個參數。隱含的決定了數據映射到新的特徵空間后的分佈,gamma越大,支持向量越小,gamma值越小,支持向量越多。

      下面我們分別調劑一下C和gamma來看一下對結果的影響。

      首先我們調節C,先做一個有噪音的數據分佈

    # n_samples=50 表示取50個點,centers=2表示將數據分為兩類
    X, y = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=0.8)
    
    plt.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
    

       結果如圖所示:

       上面的分佈看起來要劃分似乎有點困難,所以我們可以進行軟件各調整看看。

    # n_samples=50 表示取50個點,centers=2表示將數據分為兩類
    X, y = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=0.8)
    
    fig, ax = plt.subplots(1, 2, figsize=(16, 6))
    fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
    
    for axi, C in zip(ax, [10.0, 0.1]):
        model = SVC(kernel='linear', C=C)
        model.fit(X, y)
        axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
        plot_SVC_decision_function(model, axi)
        axi.scatter(model.support_vectors_[:, 0],
                    model.support_vectors_[:, 1],
                    s=300, lw=1, facecolors='none')
        axi.set_title('C={0:.1f}'.format(C), size=14)
    

       結果如圖所示:

       可以看到左邊這幅圖C值比較大,要求比較嚴格,不能分錯東西,隔離帶中沒有進入任何一個點,但是隔離帶的距離比較小,泛化能力比較差。右邊這幅圖C值比較小,要求相對來說比較松一點,隔離帶較大,但是隔離帶中進入了很多的黃點和紅點。那麼C大一些好還是小一些好呢?這需要考慮實際問題,可以進行K折交叉驗證來得到最合適的C值。

       下面再看看另一個參數gamma值,這個參數值只是在高斯核函數裏面才有,這個參數控制着模型的複雜程度,這個值越大,模型越複雜,值越小,模型就越精簡。

      代碼如下:

    # n_samples=50 表示取50個點,centers=2表示將數據分為兩類
    X, y = make_blobs(n_samples=100, centers=2, random_state=0, cluster_std=0.8)
    
    fig, ax = plt.subplots(1, 3, figsize=(16, 6))
    fig.subplots_adjust(left=0.0625, right=0.95, wspace=0.1)
    
    for axi, gamma in zip(ax, [10.0, 1.0, 0.1]):
        model = SVC(kernel='rbf', gamma=gamma)
        model.fit(X, y)
        axi.scatter(X[:, 0], X[:, 1], c=y, s=50, cmap='autumn')
        plot_SVC_decision_function(model, axi)
        axi.scatter(model.support_vectors_[:, 0],
                    model.support_vectors_[:, 1],
                    s=300, lw=1, facecolors='none')
        axi.set_title('gamma={0:.1f}'.format(gamma), size=14)
    

       結果如下:

       可以看出,當這個參數較大時,可以看出模型分類效果很好,但是泛化能力不太好。當這個參數較小時,可以看出模型裏面有些分類是錯誤的,但是這個泛化能力更好,一般也應有的更多。

      通過這個簡單的例子,我們對支持向量機在SVM中的基本使用,以及軟間隔參數的調整,還有核函數變換和gamma值等一些參數的比較。

      完整代碼請參考我的GitHub(地址:https://github.com/LeBron-Jian/MachineLearningNote)。

    9,SVM調參實例2

      下面我們用一個實例學習SVM RBF分類調參(此例子是劉建平老師的博客內容,鏈接在文後)。

      首先,我們生成一些隨機數據,為了讓數據難一點,我們加入了一些噪音,代碼如下:

    # _*_coding:utf-8_*_
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.svm import SVC
    from sklearn.datasets import make_moons, make_circles
    from sklearn.preprocessing import StandardScaler
    from matplotlib.colors import ListedColormap
    
    X, y = make_circles(noise=0.2, factor=0.5, random_state=1)
    # 對數據進行標準化
    X = StandardScaler().fit_transform(X)
    
    # 下面看看數據長什麼樣子
    cm = plt.cm.RdBu
    cm_birght = ListedColormap(['#FF0000', '#0000FF'])
    ax = plt.subplot()
    
    ax.set_title('Input data')
    # plot the training points
    ax.scatter(X[:, 0], X[:, 1], c=y, cmap=cm_birght)
    ax.set_xticks([])
    ax.set_yticks([])
    plt.tight_layout()
    plt.show()
    

      上面代碼對數據做了標準化,注意做標準化和不做標準化的差異(不一定所有的數據標準化后的效果更好,但是絕大多數確實更好)。比如下圖:

      我們看,當不做數據標準化,我們x1的取值範圍由0~90不等,當做了數據標準化之後,其取值範圍就在-2~2之間了。說明標準化的作用還是很明顯的,不多贅述,下面繼續。

       生成的數據如下(可能下一次運行,就變了哈):

       知道數據長什麼樣了,下面我們要對這個數據集進行SVM RBF分類了,分類時我們採用了網格搜索,在C=(0.1, 1, 10)和 gamma=(1, 0.1, 0.01)形成的9種情況中選擇最好的超參數,我們用了4折交叉驗證。這裏只是一個例子,實際運用中,可能需要更多的參數組合來進行調參。

      代碼及其結果如下:

    # 網格搜索尋找最佳參數
    grid = GridSearchCV(SVC(), param_grid={'C': [0.1, 1, 10], 'gamma': [1, 0.1, 0.01]}, cv=4)
    grid.fit(X, y)
    print("The best parameters are %s with a score of %0.2f"
          % (grid.best_params_, grid.best_score_))
    # The best parameters are {'C': 10, 'gamma': 0.1} with a score of 0.91
    

       就是說,我們通過網格搜索,在我們給定的9組超參數組合中,C=10, gamma=0.1 分數最高,這就是我們最終的參數候選。

      下面我們看看SVM分類后的可視化,這裏我們把上面九種組合各個訓練后,通過對網格里的點預測來標色,觀察分類的效果圖,代碼如下:

    # SVM 分類後進行可視化
    x_min, x_max = X[:, 0].min(), X[:, 0].max() + 1
    y_min, y_max = X[:, 1].min(), X[:, 1].max() + 1
    xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.02),
                         np.arange(y_min, y_max, 0.02))
    
    for i, C in enumerate((0.1, 1, 10)):
        for j, gamma in enumerate((1, 0.1, 0.01)):
            # plt.subplot()
            clf = SVC(C=C, gamma=gamma)
            clf.fit(X, y)
            Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    
            # put the result into a color plot
            Z = Z.reshape(xx.shape)
            plt.contourf(xx, yy, Z, cmap=plt.cm.coolwarm)
    
            # Plot also the training points
            plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.coolwarm)
    
            plt.xlim(xx.min(), xx.max())
            plt.ylim(yy.min(), yy.max())
            plt.xticks(())
            plt.yticks(())
            plt.xlabel(" gamma=" + str(gamma) + " C=" + str(C))
            plt.show()
    

       結果如下:

       從我測試的結果來看,劉老師的代碼還是有一點點問題,显示不出九個,所以這裏我打算重新學習一個例子。

       完整代碼請參考我的GitHub(地址:https://github.com/LeBron-Jian/MachineLearningNote)。

    10,SVM調參實例3(非線性支持向量機)

      非線性的話,我們一方面可以利用核函數構造出非線性,一方面我們可以自己構造非線性。下面首先學習自己構造非線性。

    10.1 自己構造非線性數據

      我們構造非線性數據的代碼如下:

    # _*_coding:utf-8_*_
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.svm import SVC
    from sklearn.datasets import make_moons, make_circles
    from sklearn.preprocessing import StandardScaler
    
    X1D = np.linspace(-4, 4, 9).reshape(-1, 1)
    # np.c_是按行連接兩個矩陣,就是把兩矩陣左右相加,要求行數相等。
    X2D = np.c_[X1D, X1D ** 2]
    y = np.array([0, 0, 1, 1, 1, 1, 1, 0, 0])
    
    plt.figure(figsize=(11, 4))
    
    plt.subplot(121)
    plt.grid(True, which='both')
    plt.axhline(y=0, color='k')
    plt.plot(X1D[:, 0][y == 0], np.zeros(4), 'bs')
    plt.plot(X1D[:, 0][y == 1], np.zeros(5), 'g*')
    plt.gca().get_yaxis().set_ticks([])
    plt.xlabel(r'$x_1$', fontsize=20)
    plt.axis([-4.5, 4.5, -0.2, 0.2])
    
    plt.subplot(122)
    plt.grid(True, which='both')
    plt.axhline(y=0, color='k')
    plt.axvline(x=0, color='k')
    plt.plot(X2D[:, 0][y == 0], X2D[:, 1][y == 0], 'bs')
    plt.plot(X2D[:, 0][y == 1], X2D[:, 1][y == 1], 'g*')
    plt.xlabel(r'$x_1$', fontsize=20)
    plt.ylabel(r'$x_2$', fontsize=20, rotation=0)
    plt.gca().get_yaxis().set_ticks([0, 4, 8, 12, 16])
    plt.plot([-4.5, 4.5], [6.5, 6.5], 'r--', linewidth=3)
    plt.axis([-4.5, 4.5, -1, 17])
    
    plt.subplots_adjust(right=1)
    plt.show()
    

       圖如下:

      從這個圖可以看到,我們利用對數據的變換,可以對數據的維度增加起來,變成非線性。

       假設我們不使用核函數的思想,先對數據做變換,看能不能達到一個比較好的結果,首先我們做一個測試的數據,代碼如下:

    # _*_coding:utf-8_*_
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.datasets import make_moons
    
    X, y = make_moons(n_samples=100, noise=0.15, random_state=42)
    
    
    def plot_dataset(X, y, axes):
        plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'bs')
        plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'g*')
        plt.axis(axes)
        plt.grid(True, which='both')
        plt.xlabel(r'$x_1$', fontsize=20)
        plt.ylabel(r'$x_2$', fontsize=20, rotation=0)
    
    plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
    plt.show()
    

       生成的圖如下:

       下面代碼將兩類數據分出來了:

    Polynomial_svm_clf = Pipeline((('poly_features', PolynomialFeatures(degree=3)),
                                   ('scaler', StandardScaler()),
                                   ('svm_clf', LinearSVC(C=10))
                                   ))
    Polynomial_svm_clf.fit(X, y)
    
    def plot_predictions(clf, axes):
        x0s = np.linspace(axes[0], axes[1], 100)
        x1s = np.linspace(axes[2], axes[3], 100)
        x0, x1 = np.meshgrid(x0s, x1s)
        X = np.c_[x0.ravel(), x1.ravel()]
        y_pred = clf.predict(X).reshape(x0.shape)
        # 下面填充一個等高線, alpha表示透明度
        plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)
    
    plot_predictions(Polynomial_svm_clf, [-1.5, 2.5, -1, 1.5])
    plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
    plt.show()
    

       結果如下:

       從結果來看,我們使用線性支持向量機將兩類數據區分開是沒有問題的。而最重要的是我們如何使用核函數呢?下面繼續學習

    10.2 如何對非線性數據進行核函數的變換

       我們首先看svm的官方文檔:

       核函數默認是 rbf,也就是徑向基核函數。下面分別演示核函數。

      我們依舊拿上面的數據,首先取核函數為 多項式核 看看效果(這裏對比的是多項式核的degree,也就是多項式核的維度):

    # _*_coding:utf-8_*_
    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.datasets import make_moons
    from sklearn.pipeline import Pipeline
    from sklearn.preprocessing import PolynomialFeatures
    from sklearn.preprocessing import StandardScaler
    from sklearn.svm import LinearSVC, SVC
    
    X, y = make_moons(n_samples=100, noise=0.15, random_state=42)
    
    def plot_dataset(X, y, axes):
        plt.plot(X[:, 0][y == 0], X[:, 1][y == 0], 'bs')
        plt.plot(X[:, 0][y == 1], X[:, 1][y == 1], 'g*')
        plt.axis(axes)
        plt.grid(True, which='both')
        plt.xlabel(r'$x_1$', fontsize=20)
        plt.ylabel(r'$x_2$', fontsize=20, rotation=0)
    
    # 展示圖像
    def plot_predictions(clf, axes):
        x0s = np.linspace(axes[0], axes[1], 100)
        x1s = np.linspace(axes[2], axes[3], 100)
        x0, x1 = np.meshgrid(x0s, x1s)
        X = np.c_[x0.ravel(), x1.ravel()]
        y_pred = clf.predict(X).reshape(x0.shape)
        # 下面填充一個等高線, alpha表示透明度
        plt.contourf(x0, x1, y_pred, cmap=plt.cm.brg, alpha=0.2)
    
    
    Poly_kernel_svm_clf = Pipeline((('scaler', StandardScaler()),
                                    ('svm_clf', SVC(kernel='poly', degree=3, coef0=1, C=5))
                                    ))
    Poly_kernel_svm_clf.fit(X, y)
    # 下面做一個對比試驗,看看degree的值的變換
    Poly_kernel_svm_clf_plus = Pipeline((('scaler', StandardScaler()),
                                         ('svm_clf', SVC(kernel='poly', degree=10, coef0=1, C=5))
                                         ))
    Poly_kernel_svm_clf_plus.fit(X, y)
    
    plt.subplot(121)
    plot_predictions(Poly_kernel_svm_clf, [-1.5, 2.5, -1, 1.5])
    plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
    plt.title(r'$d=3, r=1, C=5$', fontsize=18)
    
    plt.subplot(122)
    plot_predictions(Poly_kernel_svm_clf, [-1.5, 2.5, -1, 1.5])
    plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
    plt.title(r'$d=10, r=100, C=5$', fontsize=18)
    plt.show()
    

       結果如下:

       我們是把數據映射到高維空間,然後再拿回來看效果,實際上並沒有去高維空間做運算。。這就是我們想要展示的多項式核函數,下面學習高斯核函數。

      高斯核函數:利用相似度來變換特徵

      我們選擇一份一維數據,並在 x1=-2,  x1=1 處為其添加兩個高斯函數,接下來讓我門將相似度函數定義為 gamma=0.3 的徑向基核函數(RBF):

      例如: x1 = -1:它位於距第一個地標距離為1的地方,距離第二個地標距離為2。因此其新特徵為 x2 = exp(-0.3*1^2)=0.74 ,並且  x3 = exp(-0.3 * 2^2)=0.3。

      圖如下:

       這裏說一下,就是假設 X2和 X3為兩個高斯函數,我們看 x這個點距離兩個地標的距離。離高斯分佈的中心越近,就越發生什麼。。經過計算出來距離兩個地標的距離,我們就可以依此類推,來計算所有一維坐標相對應的二維坐標。(二維坐標就是距離兩個高斯函數的距離)。

      我們這裏用相似度特徵來替換原本的特徵。

      下面我們做一個實驗,我們只看 gamma的變換,高斯函數的開口變化:

    X1D = np.linspace(-4, 4, 9).reshape(-1, 1)
    X2D = np.c_[X1D, X1D ** 2]
    
    X, y = make_moons(n_samples=100, noise=0.15, random_state=42)
    
    
    def gaussian_rbf(x, landmark, gamma):
        return np.exp(-gamma * np.linalg.norm(x - landmark, axis=1) ** 2)
    
    
    gamma = 0.3
    
    # 下面進行訓練,得到一個支持向量機的模型(這裏我們沒有訓練,直接畫出來了)
    # 因為測試的數據是我們自己寫的,為了方便,我們自己畫出來,當然你也可以自己做
    xls = np.linspace(-4.5, 4.5, 200).reshape(-1, 1)
    x2s = gaussian_rbf(xls, -2, gamma)
    x3s = gaussian_rbf(xls, 1, gamma)
    
    XK = np.c_[gaussian_rbf(X1D, -2, gamma), gaussian_rbf(X2D, 1, gamma)]
    yk = np.array([0, 0, 1, 1, 1, 1, 1, 0, 0])
    
    plt.figure(figsize=(11, 4))
    
    # plt.subplot(121)
    plt.grid(True, which='both')
    plt.axhline(y=0, color='k')
    plt.scatter(x=[-2, 1], y=[0, 0], s=150, alpha=0.5, c='red')
    plt.plot(X1D[:, 0][yk == 0], np.zeros(4), 'bs')
    plt.plot(X1D[:, 0][yk == 1], np.zeros(5), 'g*')
    plt.plot(xls, x2s, 'g--')
    plt.plot(xls, x3s, 'b:')
    plt.gca().get_yaxis().set_ticks([0, 0.25, 0.5, 0.75, 1])
    plt.xlabel(r'$x_1$', fontsize=20)
    plt.ylabel(r'Similarity', fontsize=14)
    
    plt.annotate(r'$\mathbf{x}$',
                 xy=(X1D[3, 0], 0),
                 xytest=(-0.5, 0.20),
                 ha='center',
                 arrowprops=dict(facecolor='black', shrink=0.1),
                 fontsize=18,
                 )
    plt.text(-2, 0.9, "$x_2$", ha='center', fontsize=20)
    plt.text(1, 0.9, "$x_3$", ha='center', fontsize=20)
    plt.axis([-4.5, 4.5, -0.1, 1.1])
    

       結果如下(下面我們分別調試gamma,分為0.3   0.8):

       理論情況下,我們會得到怎麼維特徵呢?可以對每一個實例(樣本數據點)創建一個地標,此時會將mn 的訓練集轉換成 mm 的訓練集(m表示樣本個數,n表示特徵維度個數)。

      SVM中利用核函數的計算技巧,大大降低了計算複雜度

    • 增加gamma 使高斯曲線變窄,因此每個實例的影響範圍都較小,決策邊界最終變得不規則,在個別實例周圍擺動
    • 減少gamma 使高斯曲線變寬,因此實例具有更大的影響範圍,並且決策邊界更加平滑

       下面做一個對比試驗(gamma值(0.1  0.5), C值(0.001, 1000)):

    rbf_kernel_svm_clf = Pipeline((('scaler', StandardScaler()),
                                   ('svm_clf', SVC(kernel='rbf', gamma=5, C=0.001))
                                   ))
    
    gamma1, gamma2 = 0.1, 5
    C1, C2 = 0.001, 1000
    hyperparams = (gamma1, C1), (gamma1, C2), (gamma2, C1), (gamma2, C2)
    
    svm_clfs = []
    for gamma, C in hyperparams:
        rbf_kernel_svm_clf.fit(X, y)
        svm_clfs.append(rbf_kernel_svm_clf)
    
    plt.figure(figsize=(11, 7))
    
    for i, svm_clfs in enumerate(svm_clfs):
        plt.subplot(221 + i)
        plot_predictions(svm_clfs, [-1.5, 2.5, -1, 1.5])
        plot_dataset(X, y, [-1.5, 2.5, -1, 1.5])
        gamma, C = hyperparams[i]
        plt.title(r'$\gamma={}, C={}$'.format(gamma, C), fontsize=16)
    plt.show()
    

       結果如下:

       我們看第一幅圖,邊界比較平穩,沒有過擬合的風險,我們看當 gamma比較大的時候,過擬合的風險卻比較大了。所以說最終我們看的還是高斯函數的開口大還是小,大一點,也就是gamma小,過擬合風險小,反之同理。

      完整代碼請參考我的GitHub(地址:https://github.com/LeBron-Jian/MachineLearningNote)。

    完整代碼及其數據,請移步小編的GitHub

      傳送門:請點擊我

      如果點擊有誤:https://github.com/LeBron-Jian/MachineLearningNote

     

     

    參考文獻:https://blog.csdn.net/BIT_666/article/details/79979580

    https://www.cnblogs.com/tonglin0325/p/6107114.html

    https://cloud.tencent.com/developer/article/1146077

    https://www.cnblogs.com/xiaoyh/p/11604168.html

    https://www.cnblogs.com/pinard/p/6126077.html

    https://www.cnblogs.com/pinard/p/6117515.html

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

    【其他文章推薦】

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

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

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

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

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

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

  • Spring AOP學習筆記03:AOP的核心實現之獲取增強器

      上文講了spring是如何開啟AOP的,簡單點說就是將AnnotationAwareAspectJAutoProxyCreator這個類註冊到容器中,因為這個類最終實現了BeanPostProcessor接口,並且在其postProcessAfterInitialization()方法中完成了AOP代理對象的創建,創建時機則是在bean的init方法被執行之後即bean初始化完成之後。postProcessAfterInitialization()方法是重點,本文及下一篇文章都是圍繞着這個方法來的。

      該方法是實現在其父類中的AbstractAutoProxyCreator,我們先來看一下其實現:

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean != null) {
                // 根據給定的bean的class和name構件key,格式:beanClassName_beanName
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (!this.earlyProxyReferences.containsKey(cacheKey)) {
                        // 如果它適合被代理,則在這裏面生成代理類
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    
    protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
        // 如果已經處理過則不再處理
        if (beanName != null && this.targetSourcedBeans.containsKey(beanName)) {
            return bean;
        }
        // 無需增強則直接返回
        if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
            return bean;
        }
        // 指定的bean類是否代表一個基礎設施類,基礎設施類不應被代理,或者配置了指定bean也不需要自動代理
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }
    
        // 如果存在Advice則創建代理
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        // 如果獲取到了增強則需要針對增強創建代理
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
                // 創建代理
            Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }
    
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

      在上面的函數中我們已經可以看到代理創建的雛形,當然,在真正開始之前還需要經過一系列判斷,比如是否已經處理過或者是否是需要跳過的bean,而真正創建代理是從getAdvicesAndAdvisorsForBean開始的。
      創建代理主要包含了兩個步驟:

    • 獲取增強方法或者增強器;
    • 根據獲取的增強進行代理創建;

      這裏雖然看起來只有簡單的兩步,但是每一步中都有着大量複雜的邏輯。本文先來看看獲取增強方法的實現邏輯。

    protected Object[] getAdvicesAndAdvisorsForBean(Class beanClass, String beanName, TargetSource targetSource) {
        List advisors = findEligibleAdvisors(beanClass, beanName);
        if (advisors.isEmpty()) {
            return DO_NOT_PROXY;
        }
        return advisors.toArray();
    }
    
    protected List<Advisor> findEligibleAdvisors(Class beanClass, String beanName) {
        List<Advisor> candidateAdvisors = findCandidateAdvisors();
        List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
        extendAdvisors(eligibleAdvisors);
        if (!eligibleAdvisors.isEmpty()) {
            eligibleAdvisors = sortAdvisors(eligibleAdvisors);
        }
        return eligibleAdvisors;
    }

      對於某個bean的增強方法的獲取也是包含兩個步驟的,獲取所有的增強以及尋找所有增強中適用於bean的增強並應用,而findCandidateAdvisors()與findAdvisorsThatCanApply()便是做了這兩件事情。這裏如果無法找到對應的增強器則直接返回DO_NOT_PROXY,也就是null。

    1. 獲取增強器

      這裏我們分析的是註解方式的AOP,對於findCandidateAdvisors的實現其實是由AnnotationAwareAspectJAutoProxyCreator類來完成的,繼續跟蹤:

    protected List<Advisor> findCandidateAdvisors() {
        // 使用註解方式配置AOP的時候並不會丟棄對XML配置的支持,在這裡會調用父類方法去加載配置文件中的AOP聲明
        List<Advisor> advisors = super.findCandidateAdvisors();
        // Build Advisors for all AspectJ aspects in the bean factory.
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
        return advisors;
    }

      AnnotationAwareAspectJAutoProxyCreator間接繼承了AbstractAdvisorAutoProxyCreator,在實現獲取增強的方法中除了保留父類的獲取配置文件中定義的增強外,同時增加了獲取Bean的註解增強的功能,這部分實現是由this.aspectJAdvisorsBuilder.buildAspectJAdvisors()來完成的。

      這裏其實可以自己先嘗試想象一下解析思路,看看自己的實現與Spring是否有差別?我們可以先在自己頭腦中嘗試實現一下獲取增強這個功能點,看看是否有思路。實際上,Spring在實現的時候主要分成了四步:

    • 獲取所有beanName,這一步驟中所有在beanFactory中註冊的Bean都會被提取出來;
    • 遍歷所有beanName,並找出聲明AspectJ註解的類,進行進一步的處理;
    • 對標記為AspectJ註解的類進行增強器的提取;
    • 將提取結果加入緩存;

      我們來看看Spring如何實現對所有的類進行分析並提取Advisor:

    public List<Advisor> buildAspectJAdvisors() {
        List<String> aspectNames = null;
    
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            if (aspectNames == null) {
                List<Advisor> advisors = new LinkedList<Advisor>();
                aspectNames = new LinkedList<String>();
                // 獲取所有的beanName
                String[] beanNames =
                        BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
                // 遍歷所有的beanName找出對應的增強方法
                for (String beanName : beanNames) {
                    // 不合法的bean則略過,由子類定義規則,默認返回true
                    if (!isEligibleBean(beanName)) {
                        continue;
                    }
                    // 獲取對應的bean的類型
                    Class beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }
                    // 如果該bean上存在Aspect註解
                    if (this.advisorFactory.isAspect(beanType)) {
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            MetadataAwareAspectInstanceFactory factory =
                                    new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                            // 解析增強方法
                            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                            if (this.beanFactory.isSingleton(beanName)) {
                                this.advisorsCache.put(beanName, classAdvisors);
                            }
                            else {
                                this.aspectFactoryCache.put(beanName, factory);
                            }
                            advisors.addAll(classAdvisors);
                        }
                        else {
                            // Per target or per this.
                            if (this.beanFactory.isSingleton(beanName)) {
                                throw new IllegalArgumentException("Bean with name '" + beanName +
                                        "' is a singleton, but aspect instantiation model is not singleton");
                            }
                            MetadataAwareAspectInstanceFactory factory =
                                    new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
                            this.aspectFactoryCache.put(beanName, factory);
                            advisors.addAll(this.advisorFactory.getAdvisors(factory));
                        }
                    }
                }
                this.aspectBeanNames = aspectNames;
                return advisors;
            }
        }
    
        if (aspectNames.isEmpty()) {
            return Collections.EMPTY_LIST;
        }
        // 記錄緩存
        List<Advisor> advisors = new LinkedList<Advisor>();
        for (String aspectName : aspectNames) {
            List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
            if (cachedAdvisors != null) {
                advisors.addAll(cachedAdvisors);
            }
            else {
                MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
                advisors.addAll(this.advisorFactory.getAdvisors(factory));
            }
        }
        return advisors;
    }

      到這裏已經完成了Advisor的提取,在上面步驟中最為重要也最為複雜的就是增強器的獲取,這一功能是委託給getAdvisors方法來實現(this.advisorFactory.getAdvisors(factory))。 

    public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
        // 獲取標記為AspectJ的類及其名字,並驗證
        final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
        final String aspectName = maaif.getAspectMetadata().getAspectName();
        validate(aspectClass);
    
        // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
        // so that it will only instantiate once.
        final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
                new LazySingletonAspectInstanceFactoryDecorator(maaif);
    
        final List<Advisor> advisors = new LinkedList<Advisor>();
        for (Method method : getAdvisorMethods(aspectClass)) {
            // 獲取增強器
            Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }
    
        // If it's a per target aspect, emit the dummy instantiating aspect.
        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }
    
        // Find introduction fields.
        for (Field field : aspectClass.getDeclaredFields()) {
            Advisor advisor = getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }
    
        return advisors;
    }

      上面函數中首先完成了對增強器的獲取,包括獲取註解以及根據註解生成增強的步驟,然後考慮到在配置中可能會將增強配置成延遲初始化,那麼需要在首位加入同步實例化增強器以保證增強使用之前的實例化,最後是對DeclareParents註解的獲取,這裏着重分析對增強器的獲取。
      對普通增強器的獲取邏輯是實現在getAdvisor方法中的,實現步驟包括對切點的註解的獲取以及根據註解信息生成增強。

    public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif,
            int declarationOrderInAspect, String aspectName) {
    
        validate(aif.getAspectMetadata().getAspectClass());
        // 切點信息的獲取
        AspectJExpressionPointcut ajexp =
                getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
        if (ajexp == null) {
            return null;
        }
        // 根據切點信息生成增強器
        return new InstantiationModelAwarePointcutAdvisorImpl(
                this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
    }

    1.1 切點信息的獲取

      所謂獲取切點信息就是指定註解的表達式信息的獲取,如@Before(“test()”)。

    private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
        // 獲取方法上的註解
        AspectJAnnotation<?> aspectJAnnotation =
                AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }
        // 使用AspectJExpressionPointcut實例封裝獲取的信息
        AspectJExpressionPointcut ajexp =
                new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
        // 提取得到的註解中的表達式,如:
        // @Pointcut("execution(* *.*test*(..))")中的execution(* *.*test*(..))")
        ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
        return ajexp;
    }
    
    protected static AspectJAnnotation findAspectJAnnotationOnMethod(Method method) {
        // 設置敏感的註解類
        Class<? extends Annotation>[] classesToLookFor = new Class[] {
                Before.class, Around.class, After.class, AfterReturning.class, AfterThrowing.class, Pointcut.class};
        for (Class<? extends Annotation> c : classesToLookFor) {
            AspectJAnnotation foundAnnotation = findAnnotation(method, c);
            if (foundAnnotation != null) {
                return foundAnnotation;
            }
        }
        return null;
    }
    
    // 獲取指定方法上的註解並使用AspectJAnnotation封裝
    private static <A extends Annotation> AspectJAnnotation<A> findAnnotation(Method method, Class<A> toLookFor) {
        A result = AnnotationUtils.findAnnotation(method, toLookFor);
        if (result != null) {
            return new AspectJAnnotation<A>(result);
        }
        else {
            return null;
        }
    }

    1.2 根據切點信息生成增強

      所有的增強都是由Advisor的實現類InstantiationModelAwarePointcutAdvisorImpl統一封裝的。

    public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp,
            MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName) {
    
        this.declaredPointcut = ajexp;
        this.method = method;
        this.atAspectJAdvisorFactory = af;
        this.aspectInstanceFactory = aif;
        this.declarationOrder = declarationOrderInAspect;
        this.aspectName = aspectName;
    
        if (aif.getAspectMetadata().isLazilyInstantiated()) {
            // Static part of the pointcut is a lazy type.
            Pointcut preInstantiationPointcut =
                    Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
    
            // Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
            // If it's not a dynamic pointcut, it may be optimized out
            // by the Spring AOP infrastructure after the first evaluation.
            this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif);
            this.lazy = true;
        }
        else {
            // A singleton aspect.
            this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
            this.pointcut = declaredPointcut;
            this.lazy = false;
        }
    }

      在封裝過程中只是簡單地將信息封裝在類的實例中,所有的信息單純地賦值,在實例初始化的過程中還完成了對於增強器的初始化。因為不同的增強所體現的邏輯是不同的,比如@Before(“test()”)與@After(“test()”)標籤的區別就是增強器增強的位置不同,所以就需要不同的增強器來完成不同的邏輯,而根據註解中的信息初始化對應的增強器就是在instantiateAdvice函數中實現的。

    private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
        return this.atAspectJAdvisorFactory.getAdvice(
                this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
    }
    
        public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
            MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
    
        Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
        validate(candidateAspectClass);
    
        AspectJAnnotation<?> aspectJAnnotation =
                AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
        if (aspectJAnnotation == null) {
            return null;
        }
    
        // If we get here, we know we have an AspectJ method.
        // Check that it's an AspectJ-annotated class
        if (!isAspect(candidateAspectClass)) {
            throw new AopConfigException("Advice must be declared inside an aspect type: " +
                    "Offending method '" + candidateAdviceMethod + "' in class [" +
                    candidateAspectClass.getName() + "]");
        }
    
        if (logger.isDebugEnabled()) {
            logger.debug("Found AspectJ method: " + candidateAdviceMethod);
        }
    
        AbstractAspectJAdvice springAdvice;
        // 根據不同的註解類型封裝不同的增強器
        switch (aspectJAnnotation.getAnnotationType()) {
            case AtBefore:
                springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
                break;
            case AtAfter:
                springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
                break;
            case AtAfterReturning:
                springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
                AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                    springAdvice.setReturningName(afterReturningAnnotation.returning());
                }
                break;
            case AtAfterThrowing:
                springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
                AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
                if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                    springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
                }
                break;
            case AtAround:
                springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
                break;
            case AtPointcut:
                if (logger.isDebugEnabled()) {
                    logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
                }
                return null;
            default:
                throw new UnsupportedOperationException(
                        "Unsupported advice type on method " + candidateAdviceMethod);
        }
    
        // Now to configure the advice...
        springAdvice.setAspectName(aspectName);
        springAdvice.setDeclarationOrder(declarationOrderInAspect);
        String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
        if (argNames != null) {
            springAdvice.setArgumentNamesFromStringArray(argNames);
        }
        springAdvice.calculateArgumentBindings();
        return springAdvice;
    }

      從上面可以看到,Spring會根據不同的註解生成不同的增強器,例如AtBefore會對應AspectJMethodBeforeAdvice,在AspectJMethodBeforeAdvice中完成了增強方法的邏輯。本文嘗試分析幾個常用的增強器實現。

    AspectJMethodBeforeAdvice

      這是前置增強器,在Spring中,它會封裝到MethodBeforeAdviceInterceptor類的內部,這個是一個攔截器,會被放在攔截器鏈中,在創建代理bean時會用到:

    public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable {
    
        private MethodBeforeAdvice advice;
    
        /**
         * Create a new MethodBeforeAdviceInterceptor for the given advice.
         * @param advice the MethodBeforeAdvice to wrap
         */
        public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
            Assert.notNull(advice, "Advice must not be null");
            this.advice = advice;
        }
    
        public Object invoke(MethodInvocation mi) throws Throwable {
            this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() );
            return mi.proceed();
        }
    }

      其中的屬性MethodBeforeAdvicce就代表着前置增強的AspectJMethodBeforeAdvice,跟蹤其before()方法:

    public void before(Method method, Object[] args, Object target) throws Throwable {
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
    
    protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
        return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
    }
    
    protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
        Object[] actualArgs = args;
        if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
            actualArgs = null;
        }
        try {
            ReflectionUtils.makeAccessible(this.aspectJAdviceMethod);
            // 激活增強方法
            return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
        }
        catch (IllegalArgumentException ex) {
            throw new AopInvocationException("Mismatch on arguments to advice method [" +
                    this.aspectJAdviceMethod + "]; pointcut expression [" +
                    this.pointcut.getPointcutExpression() + "]", ex);
        }
        catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }

       invokeAdviceMethodWithGivenArgs方法中的aspectJAdviceMethod正是對應於前置增強的方法,在這裏實現了調用。

    AspectJAfterAdvice

      後置增強與前置增強有稍許不一致的地方。回顧上面講過的前置增強,大致的結構是在攔截器鏈中放置MethodBeforeAdviceInterceptor,並在MethodBeforeAdviceInterceptor中又放置了AspectJMethodBeforeAdvice,在調用inovke時首先串聯調用。但是在後置增強的時候卻是不一樣的,沒有提供如MethodBeforeAdviceInterceptor的中間類,而是直接實現MethodInterceptor接口,並在攔截器鏈中使用了中間的AspectAfterAdvice。

    public class AspectJAfterAdvice extends AbstractAspectJAdvice implements MethodInterceptor, AfterAdvice {
    
        public AspectJAfterAdvice(Method aspectJBeforeAdviceMethod, AspectJExpressionPointcut pointcut, AspectInstanceFactory aif) {
            super(aspectJBeforeAdviceMethod, pointcut, aif);
        }
    
        public Object invoke(MethodInvocation mi) throws Throwable {
            try {
                return mi.proceed();
            }
            finally {
                // 激活增強方法
                invokeAdviceMethod(getJoinPointMatch(), null, null);
            }
        }
    }

    2. 尋找匹配的增強器

      前面已經把所有增強器的獲取分析完了,但是對於所有增強來說,並不一定都適用於當前的Bean,還要挑選出適合的增強器,也就是滿足我們配置的通配符的增強器。這部分的具體實現在findAdvisorsThatCanApply中。

    protected List<Advisor> findAdvisorsThatCanApply(
            List<Advisor> candidateAdvisors, Class beanClass, String beanName) {
    
        ProxyCreationContext.setCurrentProxiedBeanName(beanName);
        try {
            // 過濾已經得到的advisors
            return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
        }
        finally {
            ProxyCreationContext.setCurrentProxiedBeanName(null);
        }
    }
    
    public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
        if (candidateAdvisors.isEmpty()) {
            return candidateAdvisors;
        }
        List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
                eligibleAdvisors.add(candidate);
            }
        }
        boolean hasIntroductions = !eligibleAdvisors.isEmpty();
        for (Advisor candidate : candidateAdvisors) {
            if (candidate instanceof IntroductionAdvisor) {
                // already processed
                continue;
            }
            if (canApply(candidate, clazz, hasIntroductions)) {
                eligibleAdvisors.add(candidate);
            }
        }
        return eligibleAdvisors;
    }

      findAdvisorsThatCanApply函數的主要功能是尋找所有增強器中適用於當前class的增強器,而對於真正的匹配其實是在canApply中實現的。

    public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
        if (advisor instanceof IntroductionAdvisor) {
            return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
        }
        else if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pca = (PointcutAdvisor) advisor;
            return canApply(pca.getPointcut(), targetClass, hasIntroductions);
        }
        else {
            // It doesn't have a pointcut so we assume it applies.
            return true;
        }
    }
    
    public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
        Assert.notNull(pc, "Pointcut must not be null");
        if (!pc.getClassFilter().matches(targetClass)) {
            return false;
        }
    
        MethodMatcher methodMatcher = pc.getMethodMatcher();
        IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
        if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
            introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
        }
    
        Set<Class> classes = new LinkedHashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
        classes.add(targetClass);
        for (Class<?> clazz : classes) {
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if ((introductionAwareMethodMatcher != null &&
                        introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
                        methodMatcher.matches(method, targetClass)) {
                    return true;
                }
            }
        }
    
        return false;
    }

      這裏的邏輯也不是很複雜,無非就是根據advisor中封裝的classFilter來判斷是否match對應的類。

     3. 總結

      本文主要學習了Spring AOP核心實現原理中的對增強方法的獲取,主要包含兩個步驟:

    • 獲取所有增強;
    • 尋找所有增強中適用於bean的增強;

      下文會聚焦在AOP核心實現原理的另一部分–代理的創建。

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

    【其他文章推薦】

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

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

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

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

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

  • C++ Primer Plus(一)

    完整閱讀C++ Primer Plus 

      系統重新學習C++語言部分,記錄重要但易被忽略的,關鍵但易被遺忘的。

     

    預備

      1、C++相對於C增加了最關鍵的兩項,面向對象和范型編程。

     

    處理數據

      2、對於變量明,C++沒有長度限制;同時,以兩個下劃線或一個下劃線和大寫字母開頭的名稱被保留給實現(編譯器及其使用的資源)使用;以一個下劃線開頭的名稱被保留給實現,用作全局標識符。

      3、C++11提供一種大括號初始化器,可以用它初始化任何類型。

    1 int ham = {24};
    2 int ems{7};
    3 int roc = {}; // 為0
    4 int rhs{};

      4、對於整型字面值,如果第一位為1~9則為十進制;如果第一位為0,第二位為1~7,則是八進制;如果前兩位為0x或0X,則為十六進制。如果希望使用cout輸出八進制或十六進制格式的整數,可以使用cout的控制符(這三個控制符都被包含在std命名空間)。

    1 cout << dec << a; // 10進制,默認的
    2 cout << oct << b; // 8進制
    3 cout << hex << c; // 16進制

      5、cout.put()、cin.get()的用法,可參照C語言中get()與put()的用法。

      6、C++有一種表示特殊字符的機制,他獨立於鍵盤,被稱作通用字符名。通用字符名以\u或\U開頭,前者後面是8個十六進制位,後者後面則是16個十六進制位。這些位表示的是ISO10646碼點。

      7、對於寬字符類型wcha_t,cin和cout無法很好的處理,此時應該使用wcinwcout

      8、C++11新增了char16_t和char32_t類型,C++11使用前綴u表示前者,U表示後者;並與形式為\u00F6和\U0000222B的通用字符名匹配。

    1 u'C' u“be good”   U'R' U”dirty rat”

      

    複合類型

      9、cin使用空白(空格、製表符、換行符)來確定字符串結束的位置,而cin.getline()可以依據換行符來讀取整行,並且可以制定最多讀取字符的數量。

      10、可以使用沒有名稱的結構類型,方法是省略名稱,同時定義一個結構類型和一個這種類型的變量,不常用,可用作臨時變量。

      11、C++允許對一個整數強制類型轉換為一個枚舉值,並參与賦值操作;同時可以有多個值相同的枚舉值,目前枚舉值也可以使用long,long long類型的值。對於較小的值編譯器會使用一個字節甚至更少的的字節,對於包含long類型的枚舉,會使用4個字節。

      12、在C中允許給指針直接賦字面值,但C++不允許,必須進行類型轉換。

     

    循環和關係表達式

      13、前綴遞增,前綴遞減,解除引用運算符的優先級相同,以從右往左的方式進行結合;後綴遞增,後綴遞減的優先級相同,但比前綴運算符的優先級高,以從左往右的方式結合。

      14、 cin.get(ch)和cin.get()的區別。

    屬性 cin.get(ch) cin.get()
    傳遞輸入字符的方式 賦值給參數ch 將函數返回值賦給ch
    用於字符輸入時函數的返回值  istream對象(執行bool轉換後為true)  int類型的字符編碼
    到達EOF時函數的返回值   istream對象(執行bool轉換後為false) EOF 

     

    函數——C++編程模塊

      15、如果數據類型本身並不是指針,則可以將const數據或者非const數據的地址賦給指向const的指針,但只能將非const數據的地址賦給非const指針

     

    函數探幽

      16、函數重載后,在調用函數時,如果沒有完全相同的參數類型,編譯器會做強制類型轉換進行匹配,但如果有多個可轉換的類型(也就是說有多個重載過的函數),C++將拒絕這種函數調用。

      17、函數重載的關鍵是函數的參數列表,也成為特徵標,以下兩個聲明互斥:

    1 long gronk(int n, float m);
    2 double gronk(int n, float m);

      C++不允許這種方式的重載,返回類型可以不同,但特徵標必須也不同。

      18、對於重載了引用參數的函數,C++將選擇調用最匹配的版本,這使得能夠根據參數是左值引用,const類型的左值引用還是右值引用來定製函數的行為。

      19、 函數模板並非函數定義,在使用模板時,編譯器會針對特定的類型生成函數實例,這種實例化方式被稱為隱式實例化

      20、C++允許顯式實例化,也就是說可以針對特定類型使用模板生成特定的實例。

    1 template void Swap<int>(int, int); // 用<>指定類型,在聲明前加template

      它的語義為“使用Swap()模板生成int類型的函數定義”。

      與顯式實例化不同的是,顯式具體化使用下面兩個等價的聲明之一:

    1 template<> void Swap<int>(int, int);
    2 template<> void Swap(int, int);

      它們的語義是,“不要使用Swap模板來生產函數定義,而應使用專門為int類型显示地定義的函數定義”。

      21、還可以在程序中創建顯式實例化:

    1 template <class T>
    2 T Add(T a, T b){ return a + b; }
    3 int m = 6;
    4 double n = 9.8;
    5 cout << Add<double>(m, n) << endl;

      由於這裏显示實例化中的特定類型為double,所以變量m會被強制類型轉換成double類型。

      22、對於函數重載函數模板函數模板重載,C++將選擇哪個版本?

      請看這裏———>   C++ 函數重載,函數模板和函數模板重載,選擇哪一個?

      23、C++11,在函數模板中,當無法得知一個變量的值時,可以使用decltype關鍵字來決定返回值類型:

    1 template<class T1, class T2>
    2 void ft(T1 x, T2 y)
    3 {
    4     decltype(x+y) xpy = x + y; // 此時xpy的類型就是x+y后的類型
    5 }

      24、decltype關鍵字本質上更複雜一些,編譯器必須遍歷一個核對錶去確定類型,現在有如下聲明:

    1 decltype(expression) var;

      第一,expression是一個沒有用括號括起來的標識符,則var的類型與該標識符相同,包括const等限定符。

      第二,如果expression是一個函數調用,則於與函數的返回值相同,這裏並不執行函數,只是查看返回值類型。

      第三,如果expression是一個左值,並且expression是被括號括起的,var會是引用類型,否則第一步就會處理。

      第四,到了這裏,var的類型只能與expression相同。

      25、C++11,在函數模板中,當無法得知返回值類型時,一般不可以直接使用關鍵字decltype來得到返回值類型,因為此時往往decltype後面表達式中的變量還不在作用域內,此時,需要使用後置返回類型

    1 template<class T1, class T2>
    2 auto gt(T1 x, T2 y) -> decltype(x+y) // 此時x,y已在作用域內
    3 {
    4     return x + y;
    5 }

      auto表示是一個佔位符,表示後置返回類型提供的類型。

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

  • 一篇文章搞懂filebeat(ELK)

    一篇文章搞懂filebeat(ELK)

    本文使用的filebeat是7.7.0的版本
    本文從如下幾個方面說明:

    • filebeat是什麼,可以用來幹嘛
    • filebeat的原理是怎樣的,怎麼構成的
    • filebeat應該怎麼玩

    一、filebeat是什麼

    1.1、filebeat和beats的關係

      首先filebeat是Beats中的一員。
      Beats在是一個輕量級日誌採集器,其實Beats家族有6個成員,早期的ELK架構中使用Logstash收集、解析日誌,但是Logstash對內存、cpu、io等資源消耗比較高。相比Logstash,Beats所佔系統的CPU和內存幾乎可以忽略不計。
    目前Beats包含六種工具:

    • Packetbeat:網絡數據(收集網絡流量數據)
    • Metricbeat:指標(收集系統、進程和文件系統級別的CPU和內存使用情況等數據)
    • Filebeat:日誌文件(收集文件數據)
    • Winlogbeat:windows事件日誌(收集Windows事件日誌數據)
    • Auditbeat:審計數據(收集審計日誌)
    • Heartbeat:運行時間監控(收集系統運行時的數據)

    1.2、filebeat是什麼

      Filebeat是用於轉發和集中日誌數據的輕量級傳送工具。Filebeat監視您指定的日誌文件或位置,收集日誌事件,並將它們轉發到Elasticsearch或 Logstash進行索引。

      Filebeat的工作方式如下:啟動Filebeat時,它將啟動一個或多個輸入,這些輸入將在為日誌數據指定的位置中查找。對於Filebeat所找到的每個日誌,Filebeat都會啟動收集器。每個收集器都讀取單個日誌以獲取新內容,並將新日誌數據發送到libbeat,libbeat將聚集事件,並將聚集的數據發送到為Filebeat配置的輸出。

           工作的流程圖如下:

     

    1.3、filebeat和logstash的關係

      因為logstash是jvm跑的,資源消耗比較大,所以後來作者又用golang寫了一個功能較少但是資源消耗也小的輕量級的logstash-forwarder。不過作者只是一個人,加入http://elastic.co公司以後,因為es公司本身還收購了另一個開源項目packetbeat,而這個項目專門就是用golang的,有整個團隊,所以es公司乾脆把logstash-forwarder的開發工作也合併到同一個golang團隊來搞,於是新的項目就叫filebeat了。

     

    二、filebeat原理是什麼

    2.1、filebeat的構成

      filebeat結構:由兩個組件構成,分別是inputs(輸入)和harvesters(收集器),這些組件一起工作來跟蹤文件並將事件數據發送到您指定的輸出,harvester負責讀取單個文件的內容。harvester逐行讀取每個文件,並將內容發送到輸出。為每個文件啟動一個harvester。harvester負責打開和關閉文件,這意味着文件描述符在harvester運行時保持打開狀態。如果在收集文件時刪除或重命名文件,Filebeat將繼續讀取該文件。這樣做的副作用是,磁盤上的空間一直保留到harvester關閉。默認情況下,Filebeat保持文件打開,直到達到close_inactive

    關閉harvester可以會產生的結果:

    • 文件處理程序關閉,如果harvester仍在讀取文件時被刪除,則釋放底層資源。
    • 只有在scan_frequency結束之後,才會再次啟動文件的收集。
    • 如果該文件在harvester關閉時被移動或刪除,該文件的收集將不會繼續

      一個input負責管理harvesters和尋找所有來源讀取。如果input類型是log,則input將查找驅動器上與定義的路徑匹配的所有文件,併為每個文件啟動一個harvester。每個input在它自己的Go進程中運行,Filebeat當前支持多種輸入類型。每個輸入類型可以定義多次。日誌輸入檢查每個文件,以查看是否需要啟動harvester、是否已經在運行harvester或是否可以忽略該文件

    2.2、filebeat如何保存文件的狀態

      Filebeat保留每個文件的狀態,並經常將狀態刷新到磁盤中的註冊表文件中。該狀態用於記住harvester讀取的最後一個偏移量,並確保發送所有日誌行。如果無法訪問輸出(如Elasticsearch或Logstash),Filebeat將跟蹤最後發送的行,並在輸出再次可用時繼續讀取文件。當Filebeat運行時,每個輸入的狀態信息也保存在內存中。當Filebeat重新啟動時,來自註冊表文件的數據用於重建狀態,Filebeat在最後一個已知位置繼續每個harvester。對於每個輸入,Filebeat都會保留它找到的每個文件的狀態。由於文件可以重命名或移動,文件名和路徑不足以標識文件。對於每個文件,Filebeat存儲唯一的標識符,以檢測文件是否以前被捕獲。

    2.3、filebeat何如保證至少一次數據消費

      Filebeat保證事件將至少傳遞到配置的輸出一次,並且不會丟失數據。是因為它將每個事件的傳遞狀態存儲在註冊表文件中。在已定義的輸出被阻止且未確認所有事件的情況下,Filebeat將繼續嘗試發送事件,直到輸出確認已接收到事件為止。如果Filebeat在發送事件的過程中關閉,它不會等待輸出確認所有事件后再關閉。當Filebeat重新啟動時,將再次將Filebeat關閉前未確認的所有事件發送到輸出。這樣可以確保每個事件至少發送一次,但最終可能會有重複的事件發送到輸出。通過設置shutdown_timeout選項,可以將Filebeat配置為在關機前等待特定時間

    三、filebeat怎麼玩

    3.1、壓縮包方式安裝

    本文採用壓縮包的方式安裝,linux版本,filebeat-7.7.0-linux-x86_64.tar.gz

    curl-L-Ohttps://artifacts.elastic.co/downloads/beats/filebeat/filebeat-7.7.0-linux-x86_64.tar.gz
    tar -xzvf filebeat-7.7.0-linux-x86_64.tar.gz

    配置示例文件:filebeat.reference.yml(包含所有未過時的配置項)
    配置文件:filebeat.yml

    3.2、基本命令

    詳情見官網:https://www.elastic.co/guide/en/beats/filebeat/current/command-line-options.html

    export   #導出
    run      #執行(默認執行)
    test     #測試配置
    keystore #秘鑰存儲
    modules  #模塊配置管理
    setup    #設置初始環境

    例如:./filebeat test config  #用來測試配置文件是否正確

    3.3、輸入輸出

    支持的輸入組件:

    Multilinemessages,Azureeventhub,CloudFoundry,Container,Docker,GooglePub/Sub,HTTPJSON,Kafka,Log,MQTT,NetFlow,Office365ManagementActivityAPI,Redis,s3,Stdin,Syslog,TCP,UDP(最常用的額就是log)

    支持的輸出組件:

    Elasticsearch,Logstash,Kafka,Redis,File,Console,ElasticCloud,Changetheoutputcodec(最常用的就是Elasticsearch,Logstash)

    3.4、keystore的使用

    keystore主要是防止敏感信息被泄露,比如密碼等,像ES的密碼,這裏可以生成一個key為ES_PWD,值為es的password的一個對應關係,在使用es的密碼的時候就可以使用${ES_PWD}使用

    創建一個存儲密碼的keystore:filebeat keystore create
    然後往其中添加鍵值對,例如:filebeatk eystore add ES_PWD
    使用覆蓋原來鍵的值:filebeat key store add ES_PWD–force
    刪除鍵值對:filebeat key store remove ES_PWD
    查看已有的鍵值對:filebeat key store list

    例如:後期就可以通過${ES_PWD}使用其值,例如:

    output.elasticsearch.password:”${ES_PWD}”

    3.5、filebeat.yml配置(log輸入類型為例)

    詳情見官網:https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-log.html

    type: log #input類型為log
    enable: true #表示是該log類型配置生效
    paths:     #指定要監控的日誌,目前按照Go語言的glob函數處理。沒有對配置目錄做遞歸處理,比如配置的如果是:
    - /var/log/* /*.log  #則只會去/var/log目錄的所有子目錄中尋找以".log"結尾的文件,而不會尋找/var/log目錄下以".log"結尾的文件。
    recursive_glob.enabled: #啟用全局遞歸模式,例如/foo/**包括/foo, /foo/*, /foo/*/* encoding:#指定被監控的文件的編碼類型,使用plain和utf-8都是可以處理中文日誌的
    exclude_lines: ['^DBG'] #不包含匹配正則的行
    include_lines: ['^ERR', '^WARN']  #包含匹配正則的行
    harvester_buffer_size: 16384 #每個harvester在獲取文件時使用的緩衝區的字節大小
    max_bytes: 10485760 #單個日誌消息可以擁有的最大字節數。max_bytes之後的所有字節都被丟棄而不發送。默認值為10MB (10485760)
    exclude_files: ['\.gz$']  #用於匹配希望Filebeat忽略的文件的正則表達式列表
    ingore_older: 0 #默認為0,表示禁用,可以配置2h,2m等,注意ignore_older必須大於close_inactive的值.表示忽略超過設置值未更新的
    文件或者文件從來沒有被harvester收集
    close_* #close_ *配置選項用於在特定標準或時間之後關閉harvester。 關閉harvester意味着關閉文件處理程序。 如果在harvester關閉
    後文件被更新,則在scan_frequency過後,文件將被重新拾取。 但是,如果在harvester關閉時移動或刪除文件,Filebeat將無法再次接收文件
    ,並且harvester未讀取的任何數據都將丟失。
    close_inactive  #啟動選項時,如果在制定時間沒有被讀取,將關閉文件句柄
    讀取的最後一條日誌定義為下一次讀取的起始點,而不是基於文件的修改時間
    如果關閉的文件發生變化,一個新的harverster將在scan_frequency運行后被啟動
    建議至少設置一個大於讀取日誌頻率的值,配置多個prospector來實現針對不同更新速度的日誌文件
    使用內部時間戳機制,來反映記錄日誌的讀取,每次讀取到最後一行日誌時開始倒計時使用2h 5m 來表示
    close_rename #當選項啟動,如果文件被重命名和移動,filebeat關閉文件的處理讀取
    close_removed #當選項啟動,文件被刪除時,filebeat關閉文件的處理讀取這個選項啟動后,必須啟動clean_removed
    close_eof #適合只寫一次日誌的文件,然後filebeat關閉文件的處理讀取
    close_timeout #當選項啟動時,filebeat會給每個harvester設置預定義時間,不管這個文件是否被讀取,達到設定時間后,將被關閉
    close_timeout 不能等於ignore_older,會導致文件更新時,不會被讀取如果output一直沒有輸出日誌事件,這個timeout是不會被啟動的,
    至少要要有一個事件發送,然後haverter將被關閉
    設置0 表示不啟動
    clean_inactived #從註冊表文件中刪除先前收穫的文件的狀態
    設置必須大於ignore_older+scan_frequency,以確保在文件仍在收集時沒有刪除任何狀態
    配置選項有助於減小註冊表文件的大小,特別是如果每天都生成大量的新文件
    此配置選項也可用於防止在Linux上重用inode的Filebeat問題
    clean_removed #啟動選項后,如果文件在磁盤上找不到,將從註冊表中清除filebeat
    如果關閉close removed 必須關閉clean removed
    scan_frequency #prospector檢查指定用於收穫的路徑中的新文件的頻率,默認10s
    tail_files:#如果設置為true,Filebeat從文件尾開始監控文件新增內容,把新增的每一行文件作為一個事件依次發送,
    而不是從文件開始處重新發送所有內容。
    symlinks:#符號鏈接選項允許Filebeat除常規文件外,可以收集符號鏈接。收集符號鏈接時,即使報告了符號鏈接的路徑,
    Filebeat也會打開並讀取原始文件。
    backoff: #backoff選項指定Filebeat如何积極地抓取新文件進行更新。默認1s,backoff選項定義Filebeat在達到EOF之後
    再次檢查文件之間等待的時間。
    max_backoff: #在達到EOF之後再次檢查文件之前Filebeat等待的最長時間
    backoff_factor: #指定backoff嘗試等待時間幾次,默認是2
    harvester_limit:#harvester_limit選項限制一個prospector并行啟動的harvester數量,直接影響文件打開數
    
    tags #列表中添加標籤,用過過濾,例如:tags: ["json"]
    fields #可選字段,選擇額外的字段進行輸出可以是標量值,元組,字典等嵌套類型
    默認在sub-dictionary位置
    filebeat.inputs:
    fields:
    app_id: query_engine_12
    fields_under_root #如果值為ture,那麼fields存儲在輸出文檔的頂級位置
    
    multiline.pattern #必須匹配的regexp模式
    multiline.negate #定義上面的模式匹配條件的動作是 否定的,默認是false
    假如模式匹配條件'^b',默認是false模式,表示講按照模式匹配進行匹配 將不是以b開頭的日誌行進行合併
    如果是true,表示將不以b開頭的日誌行進行合併
    multiline.match # 指定Filebeat如何將匹配行組合成事件,在之前或者之後,取決於上面所指定的negate
    multiline.max_lines #可以組合成一個事件的最大行數,超過將丟棄,默認500
    multiline.timeout #定義超時時間,如果開始一個新的事件在超時時間內沒有發現匹配,也將發送日誌,默認是5s
    max_procs #設置可以同時執行的最大CPU數。默認值為系統中可用的邏輯CPU的數量。
    name #為該filebeat指定名字,默認為主機的hostname
     

    3.6、實例一:logstash作為輸出

    filebeat.yml配置

    #=========================== Filebeat inputs =============================
    
    filebeat.inputs:
    
    # Each - is an input. Most options can be set at the input level, so
    # you can use different inputs for various configurations.
    # Below are the input specific configurations.
    
    - type: log
    
      # Change to true to enable this input configuration.
      enabled: true
    
      # Paths that should be crawled and fetched. Glob based paths.
      paths:  #配置多個日誌路徑
        - /var/logs/es_aaa_index_search_slowlog.log
        - /var/logs/es_bbb_index_search_slowlog.log
        - /var/logs/es_ccc_index_search_slowlog.log
        - /var/logs/es_ddd_index_search_slowlog.log
        #- c:\programdata\elasticsearch\logs\*
    
      # Exclude lines. A list of regular expressions to match. It drops the lines that are
      # matching any regular expression from the list.
      #exclude_lines: ['^DBG']
    
      # Include lines. A list of regular expressions to match. It exports the lines that are
      # matching any regular expression from the list.
      #include_lines: ['^ERR', '^WARN']
    
      # Exclude files. A list of regular expressions to match. Filebeat drops the files that
      # are matching any regular expression from the list. By default, no files are dropped.
      #exclude_files: ['.gz$']
    
      # Optional additional fields. These fields can be freely picked
      # to add additional information to the crawled log files for filtering
      #fields:
      #  level: debug
      #  review: 1
    
      ### Multiline options
    
      # Multiline can be used for log messages spanning multiple lines. This is common
      # for Java Stack Traces or C-Line Continuation
    
      # The regexp Pattern that has to be matched. The example pattern matches all lines starting with [
      #multiline.pattern: ^\[
    
      # Defines if the pattern set under pattern should be negated or not. Default is false.
      #multiline.negate: false
    
      # Match can be set to "after" or "before". It is used to define if lines should be append to a pattern
      # that was (not) matched before or after or as long as a pattern is not matched based on negate.
      # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash
      #multiline.match: after
    
    
    #================================ Outputs =====================================
    
    #----------------------------- Logstash output --------------------------------
    output.logstash:
      # The Logstash hosts #配多個logstash使用負載均衡機制
      hosts: ["192.168.110.130:5044","192.168.110.131:5044","192.168.110.132:5044","192.168.110.133:5044"]  
      loadbalance: true  #使用了負載均衡
    
      # Optional SSL. By default is off.
      # List of root certificates for HTTPS server verifications
      #ssl.certificate_authorities: ["/etc/pki/root/ca.pem"]
    
      # Certificate for SSL client authentication
      #ssl.certificate: "/etc/pki/client/cert.pem"
    
      # Client Certificate Key
      #ssl.key: "/etc/pki/client/cert.key"

    ./filebeat -e   #啟動filebeat

    logstash的配置

    input {
      beats {
        port => 5044   
      }
    }
    
    output {
      elasticsearch {
        hosts => ["http://192.168.110.130:9200"] #這裏可以配置多個
        index => "query-%{yyyyMMdd}" 
      }
    }

     

    3.7、實例二:elasticsearch作為輸出

    filebeat.yml的配置:

    ###################### Filebeat Configuration Example #########################
    
    # This file is an example configuration file highlighting only the most common
    # options. The filebeat.reference.yml file from the same directory contains all the
    # supported options with more comments. You can use it as a reference.
    #
    # You can find the full configuration reference here:
    # https://www.elastic.co/guide/en/beats/filebeat/index.html
    
    # For more available modules and options, please see the filebeat.reference.yml sample
    # configuration file.
    
    #=========================== Filebeat inputs =============================
    
    filebeat.inputs:
    
    # Each - is an input. Most options can be set at the input level, so
    # you can use different inputs for various configurations.
    # Below are the input specific configurations.
    
    - type: log
    
      # Change to true to enable this input configuration.
      enabled: true
    
      # Paths that should be crawled and fetched. Glob based paths.
      paths:
        - /var/logs/es_aaa_index_search_slowlog.log
        - /var/logs/es_bbb_index_search_slowlog.log
        - /var/logs/es_ccc_index_search_slowlog.log
        - /var/logs/es_dddd_index_search_slowlog.log
        #- c:\programdata\elasticsearch\logs\*
    
      # Exclude lines. A list of regular expressions to match. It drops the lines that are
      # matching any regular expression from the list.
      #exclude_lines: ['^DBG']
    
      # Include lines. A list of regular expressions to match. It exports the lines that are
      # matching any regular expression from the list.
      #include_lines: ['^ERR', '^WARN']
    
      # Exclude files. A list of regular expressions to match. Filebeat drops the files that
      # are matching any regular expression from the list. By default, no files are dropped.
      #exclude_files: ['.gz$']
    
      # Optional additional fields. These fields can be freely picked
      # to add additional information to the crawled log files for filtering
      #fields:
      #  level: debug
      #  review: 1
    
      ### Multiline options
    
      # Multiline can be used for log messages spanning multiple lines. This is common
      # for Java Stack Traces or C-Line Continuation
    
      # The regexp Pattern that has to be matched. The example pattern matches all lines starting with [
      #multiline.pattern: ^\[
    
      # Defines if the pattern set under pattern should be negated or not. Default is false.
      #multiline.negate: false
    
      # Match can be set to "after" or "before". It is used to define if lines should be append to a pattern
      # that was (not) matched before or after or as long as a pattern is not matched based on negate.
      # Note: After is the equivalent to previous and before is the equivalent to to next in Logstash
      #multiline.match: after
    
    
    #============================= Filebeat modules ===============================
    
    filebeat.config.modules:
      # Glob pattern for configuration loading
      path: ${path.config}/modules.d/*.yml
    
      # Set to true to enable config reloading
      reload.enabled: false
    
      # Period on which files under path should be checked for changes
      #reload.period: 10s
    
    #==================== Elasticsearch template setting ==========================
    
    
    #================================ General =====================================
    
    # The name of the shipper that publishes the network data. It can be used to group
    # all the transactions sent by a single shipper in the web interface.
    name: filebeat222
    
    # The tags of the shipper are included in their own field with each
    # transaction published.
    #tags: ["service-X", "web-tier"]
    
    # Optional fields that you can specify to add additional information to the
    # output.
    #fields:
    #  env: staging
    
    #cloud.auth:
    
    #================================ Outputs =====================================
    
    
    #-------------------------- Elasticsearch output ------------------------------
    output.elasticsearch:
      # Array of hosts to connect to.
      hosts: ["192.168.110.130:9200","92.168.110.131:9200"]
    
      # Protocol - either `http` (default) or `https`.
      #protocol: "https"
    
      # Authentication credentials - either API key or username/password.
      #api_key: "id:api_key"
      username: "elastic"
      password: "${ES_PWD}"   #通過keystore設置密碼

    ./filebeat -e   #啟動filebeat

    查看elasticsearch集群,有一個默認的索引名字filebeat-%{[beat.version]}-%{+yyyy.MM.dd}

     

     

     

    3.8、filebeat模塊

    官網:https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules.html

    這裏我使用elasticsearch模式來解析es的慢日誌查詢,操作步驟如下,其他的模塊操作也一樣:

    前提: 安裝好Elasticsearch和kibana兩個軟件,然後使用filebeat

    具體的操作官網有:https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-modules-quickstart.html

    第一步,配置filebeat.yml文件

    #============================== Kibana =====================================
    
    # Starting with Beats version 6.0.0, the dashboards are loaded via the Kibana API.
    # This requires a Kibana endpoint configuration.
    setup.kibana:
    
      # Kibana Host
      # Scheme and port can be left out and will be set to the default (http and 5601)
      # In case you specify and additional path, the scheme is required: http://localhost:5601/path
      # IPv6 addresses should always be defined as: https://[2001:db8::1]:5601
      host: "192.168.110.130:5601"  #指定kibana
      username: "elastic"   #用戶
      password: "${ES_PWD}"  #密碼,這裏使用了keystore,防止明文密碼
    
      # Kibana Space ID
      # ID of the Kibana Space into which the dashboards should be loaded. By default,
      # the Default Space will be used.
      #space.id:
    
    #================================ Outputs =====================================
    
    # Configure what output to use when sending the data collected by the beat.
    
    #-------------------------- Elasticsearch output ------------------------------
    output.elasticsearch:
      # Array of hosts to connect to.
      hosts: ["192.168.110.130:9200","192.168.110.131:9200"]
    
      # Protocol - either `http` (default) or `https`.
      #protocol: "https"
    
      # Authentication credentials - either API key or username/password.
      #api_key: "id:api_key"
      username: "elastic"  #es的用戶
      password: "${ES_PWD}" # es的密碼
      #這裏不能指定index,因為我沒有配置模板,會自動生成一個名為filebeat-%{[beat.version]}-%{+yyyy.MM.dd}的索引

    第二步:配置elasticsearch的慢日誌路徑

    cd filebeat-7.7.0-linux-x86_64/modules.d
    

    vim  elasticsearch.yml

     

     

     

    第三步:生效es模塊

    ./filebeat modules elasticsearch

    查看生效的模塊

    ./filebeat modules list

     

     

     

    第四步:初始化環境

    ./filebeat setup -e

     

     

     

     

     第五步:啟動filebeat

    ./filebeat -e

    查看elasticsearch集群,如下圖所示,把慢日誌查詢的日誌都自動解析出來了:

     

     到這裏,elasticsearch這個module就實驗成功了

     

    參考

    官網:https://www.elastic.co/guide/en/beats/filebeat/current/index.html

     

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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