標籤: 網頁設計

  • 10萬級最多人買的SUV,換代后還能不能讓你買單?

    10萬級最多人買的SUV,換代后還能不能讓你買單?

    新車還針對霧燈區域裝飾調整,而大燈光源也更換為LED光源,照明效果更出眾。煥然一新的全新哈弗H6無疑是耐看的。要知道 耐看 向來就是比較難以保證的,在面對不斷變化的消費者眼光,懂得適時微調車型造型是一件識時務的事。

    前幾天,奔赴北京,試駕了全新哈弗H6超豪型。與全新哈弗H6超豪型近距離接觸了幾天,哈弗H6已經是一款非常熟悉的SUV車型,對於它的了解也已經比較深刻;但是一款重點的全新換代,還是會讓人有不少的期待,更何況哈弗H6是一款在銷量上讓眾多同級別競品汗顏的熱銷產品,全新換代後會呈現怎樣的產品力,不得不說十分令人期待。

    畢竟這是一場試駕為主的活動,所以更多的是衝著這款車本身的試駕感受去的,那文章的開始就先聊聊全新哈弗H6超豪型的駕駛體驗好了。

    我從來不會擔心哈弗H6的底盤質感,在第一代哈弗H6上市后我就對它厚實穩重的底盤姿態感到十分的欣喜,而全新哈弗H6的底盤在此基礎上做了更加深度和全面的優化,底盤噪音降至一個很低的水平,在行駛過程中的舒適性得以充分保障;前麥弗遜,后多連桿的前後獨立懸架將路面振動過濾得比較徹底,而且避震響應動作同樣也比較利落,在面對常見顛簸路面的時候不會有晃晃悠悠的動作,行駛姿態非常從容。

    其次動力總成的變化同樣讓人印象深刻,新的1.5T渦輪增壓發動機新增了長城自己正向研發的可變氣門升程技術,新技術的引入不僅改善了油耗水平,更在功率和峰值扭矩上做了12.7%和35.7%的提升,讓哈弗H6的綜合性能有了明顯的提高。

    雙離合變速箱的標定水平也是有了長足的進步,這款濕式雙離合變速箱在實際駕駛中可以很明顯的感知到工程師在對它進行標定的時候極大程度上考慮到了行駛平順的重要性,換擋平順程度十分友好,不會有明顯突兀的頓挫感。

    除了駕駛感受給留下不錯的印象,對哈弗H6的外觀印象也越來越好了。

    全新哈弗H6超豪型藍標版的前臉有着比較大變化,新車用上了面積更大的進氣格柵,且鍍鉻裝飾條數量增加為五條;前保險杠處的通風口更換為網狀設計;而新車尾部整體造型變化不大。整車看起來更時尚、動感了。

    至於全新哈弗H6超豪型紅標版呢?外觀也是有着不容忽視的變化。與藍標版的改變相似,紅標版新車型的進氣格柵也稍微變大了,且有着5條鍍鉻裝飾條;新車還針對霧燈區域裝飾調整,而大燈光源也更換為LED光源,照明效果更出眾。

    煥然一新的全新哈弗H6無疑是耐看的。要知道 耐看 向來就是比較難以保證的,在面對不斷變化的消費者眼光,懂得適時微調車型造型是一件識時務的事。哈弗H6就很好地做到這一點,全新哈弗H6超豪型更符合當下年輕消費者的口味,潛在消費人群無疑會也進一步擴大。

    進入車內,可以感受到全新哈弗H6超豪型的座椅包裹性有了明顯改善,增強了乘坐舒適性。不僅如此,新車內飾是蒙皮經純植物提取的香料處理了,還配有pM2.5粉塵過濾系統,車內的乘坐環境更為舒適。此外,新車還在後排空調出風口下方設計了兩個USB接口,車內配備了0.82㎡的超大全景天窗、前排座椅加熱等配置,這一切都令全新哈弗H6超豪型的駕乘舒適性達到了新的高度。

    儲物空間方面,哈弗的工程師針對換擋桿前方置物盒造型進行優化,優化后的儲物盒能容納下更多更大的隨身物品。而新車的後備廂經優化后,整體容積增大近20L,實用性更強了。

    安全,也是全新哈弗H6超豪型所注重的。新車增加了四項主動安全配置,包含半自動泊車系統、ACC自適應巡航系統、360°環視系統、FCW前碰撞預警系統+AEB自動剎車系統。此外,全新哈弗H6超豪型配備了側氣簾。主/被動安全系統更加完善的哈弗H6有助於對乘客進行全方位智能防護。

    比你優秀的人不可怕,可怕的是比你優秀的人比你更努力。哈弗H6就是那個很努力的優秀“人”。作為國產緊湊型SUV市場的常勝將軍,哈弗H6一向都是走實力派路線的。試駕過很多版本的哈弗H6,可以說是看着它將自己的小毛病一個一個地改掉,到近年來接近完美的狀態。至於經多方升級的全新哈弗H6超豪型,我們有理由相信它未來將助力哈弗H6家族繼續在SUV市場叱吒風雲。

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

    【其他文章推薦】

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

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

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

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

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

  • 又要到處浪又要省油,有這種好事?這5款車就行

    又要到處浪又要省油,有這種好事?這5款車就行

    以不到30萬的價格買到一輛混動的大7座SUV,還要什麼入門BBA。再來看看同樣換新顏、尺寸大一號的全新秦pro,新車搭載一套由BYD476ZQA型1。5T發動機、電動機和鋰電池組構成的插電式混動系統后,工信部申報綜合油耗僅為1升/百公里。

    前幾天正巧試駕了朋友剛提不久的雷克薩斯CT200h,除了非常不錯的操控感和車身動態表現之外,讓我印象最深刻的就是油耗,在一頓猛踩和駕駛模式來回切換之後,平均油耗也不過7L/百公里,我相信還有其他車主做出了更漂亮的數據。

    雖然當初一早就布局混動車型市場的CT200h並沒有因此而大賣,但發展到今天,隨着混動技術的不斷成熟和國內市場政策的風雲變幻,已經有越來越多的消費者被混動車型的諸多優勢所打動,比如省油、安靜、駕駛體驗等等。所以今天就給大家盤點一下,北京車展之後都有哪些混動車型是值得消費者密切關注的。

    1、卡羅拉/雷凌pHEV插電混動版

    卡羅拉和雷凌可以說是豐田的兩個“開掛”車型,從燃油版到HEV油電混動版(雙擎)從來都不愁賣,而此次北京車展上豐田終於兌現了兩年前的承諾,在國內正式發布了卡羅拉/雷凌pHEV插電混動版車型。

    這兩款新車可以說是含着金鑰匙出生的“富二代”,畢竟豐田多年以來經營的品牌形象、技術實力和市場口碑為這二者打下了堅實的銷量基礎,所以目測上市后月銷1萬輛是基本操作,想要入手的朋友們可要盯緊了。

    在外形上兩款新車與普通版的差別並不大,僅針對細節進行了微調,比如車頭和車尾部分都加入了藍色元素。此外,由於插電混動版搭載了更大容量的電池組,所以內部空間與普通版車型稍有差異。

    值得一提的是,這兩款車屬於中國特供車,意味着自帶銷量BUG,並且其搭載了1.8L自然吸氣發動機+電動機組,同時還配備容量為9-13kWh的鋰離子電池組,純電動條件下的續航里程可超過100km。

    這是神馬概念?起碼就目前我國市場上銷售的插電混動車型,大部分純電動續航里程都只在50-80km以內,並且考慮到緊湊級車推出pHEV版本的多是自主品牌車型,而它們的老對手軒逸、朗逸等還沒加入這個市場,所以此次亮相的卡羅拉和雷凌pHEV版本只要定價合適,銷量應該不成問題,別忘了它們還可獲得一部分政策補貼哦(雙擎版本沒有)。

    2、領克01 pHEV插電混動版

    說領克01 pHEV版之前必先提一提燃油版領克01,這款車型自去年11月上市以來便憑藉優秀的做工和沃爾沃技術的加持迅速圈粉,獲得市場的好評如潮。本以為領克會按部就班、穩紮穩打地推出新能源產品,不過眼看WEY氣勢洶洶地推出了p8之後,領克怕也是坐不太住了,於是便適時地推出了領克01 pHEV版。

    在外觀和內飾方面,該車和之前的燃油版車型基本相同,只是在前進氣格柵以及前翼子板處增加了藍色元素進行點綴,表明其新能源型的身份。而尾部右下側還貼有“01 pHEV”的字樣。

    動力方面,領克01 pHEV則搭載了1.5T發動機和電動機所組成的插電式混合動力系統,發動機最大凈功率約180馬力,動力電池為鎳鈷錳三元鋰離子電池,官方宣稱該車的百公里油耗僅為1.8L,同時其純電動模式下最大行駛里程為51km。如果想要入手朋友,其量產車型最快會在今年年內上市,所以準備好鈔票就是了。

    3、比亞迪唐DM/秦pro

    我們都知道,自從前奧迪設計師艾格入主比亞迪之後,旗下打頭陣車型瞬間秒變高富帥,就連之前被人嫌棄的“BYD”都多了一個洋氣的註釋–Build Your Dream!如果你還沒什麼畫面感的話,在此貢獻兩張圖讓大家感受一下:

    了解更多點擊視頻:

    全新一代唐从里裡外外都整了個遍,多了幾分國際范。首先在外觀上採用了家族最新的Dragon Face設計語言,整體大氣而又十分吸睛;

    內飾同樣話題性十足,其中最搶眼的部分當屬中控可90°旋轉的超級大屏,同時全液晶儀錶和藍色氛圍燈增加了車內的科技感。

    動力方面,其將搭載2.0T發動機與電動機組成的插電式混動系統,系統綜合最大功率超過500馬力,而高配車型百公里加速僅需4.5秒,咳咳划重點了–奔馳GLC43 AMG的成績為4.9秒。

    值得一提的是,新一代唐標配7座布局,其長寬高分別為4870/1940/1720mm,軸距為2820mm,相比上一代車型在軸距和寬度上有所增加,所以能帶來更寬敞的乘坐體驗。另外在此次北京車展,新一代唐公布預售價–補貼后25萬起步。港真!以不到30萬的價格買到一輛混動的大7座SUV,還要什麼入門BBA!

    再來看看同樣換新顏、尺寸大一號的全新秦pro,新車搭載一套由BYD476ZQA型1.5T發動機、電動機和鋰電池組構成的插電式混動系統后,工信部申報綜合油耗僅為1升/百公里!並且根據車尾標判斷,新車的官方百公里加速時間或為5.9秒,看來是充分體現了什麼叫“雨露均沾”了。

    4、廣汽謳歌CDX混動版

    每每提到謳歌這個品牌,總有兩道跨不過的坎,一個是跟長安LOGO抹不去的姻緣,另一個就是跟隔壁家的雷克薩斯比境遇,很顯然以上兩項都是招黑的。不過也必須承認,痛定思痛的謳歌如今似乎也有點重新步入正軌的意思了。

    在此次北京車展,廣汽謳歌就帶來了CDX的混動版本,並且從售價上看還是蠻有誠意的。新車共推出了三款車型,售價區間為29.98-35.28萬元,同時新車外觀顏色增加磨砂皓灰和混動版專屬的藍紫色車漆,內飾則增加了紅色內飾可選,而頂配版車型還將增配Acura Watch。

    畢竟謳歌也是“本田大法”的受益者,所以混動版車型相比同級別對手的較大優勢就體現在動力系統上。新車將搭載本田的i-MMD混合動力系統,其由一台2.0L的阿特金森循環自然吸氣發動機、由驅動電機和發電機組成的“電動CVT”直驅傳動機構、動力控制單元及鋰電池組等組成。

    這套系統可以根據負載和使用條件的不同,在純電動、混合動力和發動機直驅三種模式之間無縫切換。參數方面,2.0L發動機最大功率146馬力,混動系統綜合最大功率215馬力,而官方公布的綜合油耗為5.0L/百公里。所以三十萬買一台省油的豪華SUV,好像很不錯的樣子!

    5、雷克薩斯ES混動版

    此次北京車展雷克薩斯的展台只放了兩台車,沒錯!只有兩台!在大多數人看來這就是典型的“寒酸”,而在雷粉眼裡這就叫“傲嬌”,因為雷克薩斯選擇把ES混動版的全球首發放在了中國,雖說一直不願意國產,雖說看展的觀眾不一定get到這樣做的用心良苦,但足以說明其對中國市場的看重。

    雷克薩斯全新一代ES基於TNGA平台打造,除搭載全新2.0L發動機外,還配有2.5L多級混合動力系統,也就是全新凱美瑞上用的那一套,沒錯,就是傳說中41%熱效率的那一款名機。同時它還搭載了結構更緊湊的全新一代E-CVT电子無極變速系統,以及布局更為合理的鎳氫電池組。另外,最高純電巡航時速由前一代的75km/h提升到120km/h。

    如想了解更多,點擊下方視頻:

    結語

    從當初雷克薩斯CT200h的“天生異質”,到如今各家廠商混合動力車型的日漸成熟,可以說能活下來的、能在市場上銷售的技術都是好技術。不管是自主品牌的比亞迪、長城、吉利,還是合資品牌的兩田,甚至是進口品牌,都在加緊布局新能源戰線,所以這對我們消費者來說無疑是個大利好。至於混動車型值不值得買這樣俗套的話題,我想就不用多糾結了,畢竟買車從來都是按需購買的。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

    ※超省錢租車方案

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

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

    不僅如此,華晨中華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地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

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

  • 不如16.98萬的國產SUV純粹?2.0T+9AT的7座SUV表示不屑

    不如16.98萬的國產SUV純粹?2.0T+9AT的7座SUV表示不屑

    實在是難為了造車的車企,由此催生出的“全能选手”也是越來越多。

    大家發現了么,現在純粹的車好像越來越少了。

    前些年總流行混搭、串燒,如今說的好聽了一些叫跨界、融合,車型上有SUV跨界轎車,更有SUV跨界MpV;定位上更是注重全面,要運動也要舒適,想豪華也想家用,求動力強也求夠省油…實在是難為了造車的車企,由此催生出的“全能选手”也是越來越多…

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

    【其他文章推薦】

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

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

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

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

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

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

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

    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/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

  • 這款帥氣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/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

  • 如何使用 Shell 腳本來查看多個服務器的端口是否打開?

    如何使用 Shell 腳本來查看多個服務器的端口是否打開?

    我們在進行服務器配置的時候,經常要查看服務器的某個端口是否已經開放。如果服務器只有一兩台的話,那很好辦,只需要使用 nc 命令一個個查看即可。

    但是,如果你的服務器是個集群,有很多台呢?那如果還一個個手動去檢查的話,效率肯定是無比低下的,年底裁員名單里肯定有你。

    在這種情況下,我們完全可以使用 Shell 腳本配合 nc 命令來達到我們的目的。而且,不管服務器有幾台,需要檢查的端口有幾個,都可以實現這樣的目標。

    在本文里,我們用 Shell 腳本來實現兩個需求:

    • 掃描多台服務器的一個端口是否打開
    • 掃描多台服務器的多個端口是否打開

    在開始之前,我們先來了解一下 nc 命令。

    nc 命令簡介

    nc 是英文單詞 netcat 的縮寫,它是通過使用 TCP 或 UDP 的網絡協議的連接來讀或寫數據,可以直接被第三方程序或腳本直接調用。

    同時,它是一款功能非常強大的網絡調試工具,因為它可以創建幾乎所有你所需要的連接方式。

    nc 工具主要有三種功能模式:連接模式、監聽模式、通道模式。它的一般使用格式如下:

    $ nc [-options] [HostName or IP] [PortNumber]
    

    接下來,我們就用 Shell 腳本結合 nc 命令來實現我們的兩個需求。

    1. 掃描多台服務器的一個端口是否打開

    在這裏,我們先把需要查詢的所有服務器地址全部放在一個 server-list.txt 文件里,每個地址單獨一行,如下:

    # cat server-list.txt
    192.168.1.2
    192.168.1.3
    192.168.1.4
    192.168.1.5
    192.168.1.6
    192.168.1.7
    

    然後,我們再用 for 循環依次掃描 server-list.txt 里對應服務器的端口是否打開。在這裏,我們掃描 22 端口是否打開。

    # vi port_scan.sh
    
    #!/bin/sh
    for server in `more server-list.txt`
    do
    #echo $i
    nc -zvw3 $server 22
    done
    

    最後,我們給這個腳本賦予可執行權限即可。

    $ chmod +x port_scan.sh
    

    之後,我們就可以用這個腳本來自動依次檢查多個服務器的 22 端口是否已打開。

    # sh port_scan.sh
    
    Connection to 192.168.1.2 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.3 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.4 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.5 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.6 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.7 22 port [tcp/ssh] succeeded!
    

    2. 掃描多台服務器的多個端口是否打開

    在這裏,我們同樣把需要查詢的所有服務器地址全部放在一個 server-list.txt 文件里,每個地址單獨一行。這裏就不重複演示了。

    與此同時,我們也把需要查詢的服務器端口放在另一個 port-list.txt 文件里,每個端口單獨一行,如下所示:

    # cat port-list.txt
    22
    80
    

    然後,我們再用 for 循環依次掃描 server-list.txt 里對應服務器 port-list.txt 所列的端口是否打開。注意,這裏用到了兩個 for 循環,第一層是服務器列表,第二層是端口列表。

    # vi multiple_port_scan.sh
    
    #!/bin/sh
    for server in `more server-list.txt`
    do
    for port in `more port-list.txt`
    do
    #echo $server
    nc -zvw3 $server $port
    echo ""
    done
    done
    

    最後,我們給這個腳本賦予可執行權限即可。

    $ chmod +x multiple_port_scan.sh
    

    之後,我們就可以用這個腳本來自動依次檢查多個服務器的多個端口是否已打開。

    # sh multiple_port_scan.sh
    Connection to 192.168.1.2 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.2 80 port [tcp/http] succeeded!
    
    Connection to 192.168.1.3 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.3 80 port [tcp/http] succeeded!
    
    Connection to 192.168.1.4 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.4 80 port [tcp/http] succeeded!
    
    Connection to 192.168.1.5 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.5 80 port [tcp/http] succeeded!
    
    Connection to 192.168.1.6 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.6 80 port [tcp/http] succeeded!
    
    Connection to 192.168.1.7 22 port [tcp/ssh] succeeded!
    Connection to 192.168.1.7 80 port [tcp/http] succeeded!
    

    公眾號:良許Linux

    有收穫?希望老鐵們來個三連擊,給更多的人看到這篇文章

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

    【其他文章推薦】

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

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

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

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

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

  • 隨機抽樣一致性(RANSAC)算法詳解

    隨機抽樣一致性(RANSAC)算法詳解

    隨機抽樣一致性(RANSAC)算法能夠有效的剔除特徵匹配中的錯誤匹配點。

    實際上,RANSAC能夠有效擬合存在噪聲模型下的擬合函數。實際上,RANSAC算法的核心在於將點劃分為“內點”和“外點”。在一組包含“外點”的數據集中,採用不斷迭代的方法,尋找最優參數模型,不符合最優模型的點,被定義為“外點”。這就是RANSAC的核心思想。

    RANSAC原理

    OpenCV中濾除誤匹配對採用RANSAC算法尋找一個最佳單應性矩陣H,矩陣大小為3×3。RANSAC目的是找到最優的參數矩陣使得滿足該矩陣的數據點個數最多,通常令h33=1來歸一化矩陣。由於單應性矩陣有8個未知參數,至少需要8個線性方程求解,對應到點位置信息上,一組點對可以列出兩個方程,則至少包含4組匹配點對

     

     

     RANSAC算法從匹配數據集中隨機抽出4個樣本並保證這4個樣本之間不共線,計算出單應性矩陣,然後利用這個模型測試所有數據,並計算滿足這個模型數據點的個數與投影誤差(即代價函數),若此模型為最優模型,則對應的代價函數最小。

    損失函數:

     

     

     也就是通過隨機抽樣求解得到一個矩陣,然後驗證其他的點是否符合模型,然後符合的點成為“內點”,不符合的點成為“外點”。下次依然從“新的內點集合”中抽取點構造新的矩陣,重新計算誤差。最後誤差最小,點數最多就是最終的模型。

    RANSAC算法步驟:

    RANSAC算法步驟: 

              1. 隨機從數據集中隨機抽出4個樣本數據 (此4個樣本之間不能共線),計算出變換矩陣H,記為模型M;

              2. 計算數據集中所有數據與模型M的投影誤差,若誤差小於閾值,加入內點集 I ;

              3. 如果當前內點集 I 元素個數大於最優內點集 I_best , 則更新 I_best = I,同時更新迭代次數k ;

              4. 如果迭代次數大於k,則退出 ; 否則迭代次數加1,並重複上述步驟;

      注:迭代次數k在不大於最大迭代次數的情況下,是在不斷更新而不是固定的;

     

     

     其中,p為置信度,一般取0.995;w為”內點”的比例 ; m為計算模型所需要的最少樣本數=4;
    關於RANSAC算法的思想,可以用下圖表示

     

     也就是RANSAC算法的本質是:在存在噪聲的數據中,我們求解一個模型,使得非噪聲數據可以用該模型表示,而噪聲數據被排除在外。

    分享三個講解RANSAC算法的網址:

    https://www.csdn.net/gather_2d/MtjaMg3sNDAwNS1ibG9n.html

    https://www.cnblogs.com/xrwang/archive/2011/03/09/ransac-1.html

    https://blog.csdn.net/yanghan742915081/article/details/83005442

     

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

    【其他文章推薦】

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

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

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

    ※超省錢租車方案

  • 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地圖已可更新顯示潭子電動車充電站設置地點!!

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

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

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

  • C++值元編程

    C++值元編程

    ——永遠不要在OJ上使用值元編程,過於簡單的沒有優勢,能有優勢的編譯錯誤。

    背景

    2019年10月,我在學習算法。有一道作業題,輸入規模很小,可以用打表法解決。具體方案有以下三種:

    1. 運行時預處理,生成所需的表格,根據輸入直接找到對應項,稍加處理后輸出;

    2. 一個程序生成表格,作為提交程序的一部分,後續與方法1相同,這樣就省去了運行時計算的步驟;

    3. 以上兩種方法結合,編譯期計算表格,運行時直接查詢,即元編程(metaprogramming)。

    做題當然是用方法1或2,但是元編程已經埋下了種子。時隔大半年,我來補上這個坑。

    題目

    北京大學OpenJudge 百練4119 複雜的整數劃分問題

    描述

    將正整數 \(n\) 表示成一系列正整數之和,\(n = n_1 + n_2 + … + n_k\),其中 \(n_1 \geq n_2 \geq … \geq n_k \geq 1\)\(k \geq 1\)。正整數 \(n\) 的這種表示稱為正整數 \(n\) 的劃分。

    輸入

    標準的輸入包含若干組測試數據。每組測試數據是一行輸入數據,包括兩個整數 \(N\)\(K\)。( \(0 \le N \leq 50\)\(0 \le K \leq N\)

    輸出

    對於每組測試數據,輸出以下三行數據:

    第一行: \(N\) 劃分成 \(K\) 個正整數之和的劃分數目

    第二行: \(N\) 劃分成若干個不同正整數之和的劃分數目

    第三行: \(N\) 劃分成若干個奇正整數之和的劃分數目

    樣例輸入

    5 2
    

    樣例輸出

    2
    3
    3
    

    提示

    第一行: 4+1,3+2

    第二行: 5,4+1,3+2

    第三行: 5,1+1+3,1+1+1+1+1+1

    解答

    標準的動態規劃題。用dp[c][i][j]表示把i分成c個正整數之和的方法數,其中每個數都不超過j

    第一行。初始化:由 \(i \leq j\) 是否成立決定dp[1][i][j]的值,當 \(i \leq j\) 時為1,劃分為 \(i = i\),否則無法劃分,值為0

    遞推:為了求dp[c][i][j],對 \(i = i_1 + i_2 + … + i_c\)\(i_1 \geq i_2 \geq … \geq i_c\) 中的最大數 \(i_1\) 分類討論,最小為 \(1\),最大不超過 \(i – 1\),因為 \(c \geq 2\),同時不超過 \(j\),因為定義。最大數為 \(n\) 時,對於把 \(i – n\) 分成 \(c – 1\) 個數,每個數不超過 \(n\) 的劃分,追加上 \(n\) 可得 \(i\) 的一個劃分。\(n\) 只有這些取值,沒有漏;對於不同的 \(n\),由於最大數不一樣,兩個劃分也不一樣,沒有多。故遞推式為:

    \[dp[c][i][j] = \sum_{n=1}^{min\{i-1,j\}}dp[c-1][i-n][n] \]

    dp[K][N][N]即為所求ans1[K][N]

    第二行。可以把遞推式中的dp[c - 1][i - n][n]修改為dp[c - 1][i - n][n - 1]后重新計算。由於只需一個與c無關的結果,可以省去c這一維度,相應地改變遞推順序,每輪累加。

    另一種方法是利用已經計算好的ans1數組。設 \(i = i_1 + i_2 + … + + i_{c-1} + i_c\),其中 \(i_1 \ge i_2 \ge … \ge i_{c+1} \ge i_c \ge 0\),則 \(i_1 – \left( c-1 \right) \geq i_2 – \left( c-2 \right) \geq … \geq i_{c-1} – 1 \geq i_c \ge 0\),且 \(\left( i_1 – \left( c-1 \right) \right) + \left( i_2 – \left( c-2 \right) \right) + … + \left( i_{c-1} – 1 \right) + \left( i_c \right) = i – \frac {c \left( c-1 \right)} {2}\),故把i劃分成c個不同正整數之和的劃分數目等於ans[c][i - c * (c - 1) / 2],遍歷c累加即得結果。

    第三行。想法與第二行相似,也是找一個對應,此處從略。另外,數學上可以證明,第二行和第三行的結果一定是一樣的。

    #include <iostream>
    #include <algorithm>
    
    constexpr int max = 50;
    int dp[max + 1][max + 1][max + 1] = { 0 };
    int ans1[max + 1][max + 1] = { 0 };
    int ans2[max + 1] = { 0 };
    int ans3[max + 1] = { 0 };
    
    int main()
    {
        int num, k;
        for (int i = 1; i <= max; ++i)
            for (int j = 1; j <= max; ++j)
                dp[1][i][j] = i <= j;
        for (int cnt = 2; cnt <= max; ++cnt)
            for (int i = 1; i <= max; ++i)
                for (int j = 1; j <= max; ++j)
                {
                    auto min = std::min(i - 1, j);
                    for (int n = 1; n <= min; ++n)
                        dp[cnt][i][j] += dp[cnt - 1][i - n][n];
                }
        for (int cnt = 1; cnt <= max; ++cnt)
            for (int i = 1; i <= max; ++i)
                ans1[cnt][i] = dp[cnt][i][i];
        for (int i = 1; i <= max; ++i)
            for (int cnt = 1; cnt <= i; ++cnt)
            {
                int j = i - cnt * (cnt - 1) / 2;
                if (j <= 0)
                    break;
                ans2[i] += ans1[cnt][j];
            }
        for (int i = 1; i <= max; ++i)
            for (int cnt = 1; cnt <= i; ++cnt)
            {
                int j = i + cnt;
                if (j % 2)
                    continue;
                j /= 2;
                ans3[i] += ans1[cnt][j];
            }
        
        while (std::cin >> num)
        {
            std::cin >> k;
            std::cout << ans1[k][num] << std::endl;
            std::cout << ans2[num] << std::endl;
            std::cout << ans3[num] << std::endl;
        }
    }
    

    值元編程基礎

    元編程是指計算機程序能把其他程序作為它們的數據的編程技術。在目前的C++中,元編程體現為用代碼生成代碼,包括宏與模板。當我們使用了std::vector<int>中的任何一個名字時,std::vector類模板就用模板參數int, std::allocator<int>實例化為std::vector<int, std::allocator<int>>模板類,這是一種元編程,不過我們通常不這麼講。

    狹義的C++模板元編程(template metaprogramming,TMP)包括值元編程、類型元編程,以及兩者的相交。本文討論的是值元編程,即為編譯期值編程。

    在C++中有兩套工具可用於值元編程:模板和constexpr。C++模板是圖靈完全的,這是模板被引入C++以後才被發現的,並不是C++模板的初衷,因此用模板做計算在C++中算不上一等用法,導致其語法比較冗長複雜。constexpr的初衷是提供純正的編譯期常量,後來才取消對計算的限制,但不能保證計算一定在編譯期完成。總之,這兩套工具都不完美,所以本文都會涉及。

    嚴格來說,constexpr不符合上述對元編程的定義,但它確實可以提供運行時程序需要的數據,所以也歸入元編程的類別。

    constexpr式值元編程

    constexpr開始講,是因為它與我們在C++中慣用的編程範式——過程式範式是一致的。

    constexpr關鍵字在C++11中被引入。當時,constexpr函數中只能包含一條求值語句,就是return語句,返回值可以用於初始化constexpr變量,作模板參數等用途。如果需要分支語句,用三目運算符?:;如果需要循環語句,用函數遞歸實現。比如,計算階乘:

    constexpr int factorial(int n)
    {
        return n <= 1 ? 1 : (n * factorial(n - 1));
    }
    

    對於編譯期常量ifactorial(i)產生編譯期常量;對於運行時值jfactorial(j)產生運行時值,也就是說,constexpr可以視為對既有函數的附加修飾。

    然而,多數函數不止有一句return語句,constexpr對函數體的限制使它很難用於中等複雜的計算任務,為此C++14放寬了限制,允許定義局部變量,允許if-elseswitch-casewhilefor等控制流。factorial函數可以改寫為:

    constexpr int factorial(int n)
    {
        int result = 1;
        for (; n > 1; --n)
            result *= n;
        return result;
    }
    

    也許你會覺得factorial函數的遞歸版本比循環版本易懂,那是因為你學習遞歸時接觸的第一個例子就是它。對於C++開發者來說,大多數情況下首選的還是循環。

    計算單個constexpr值用C++14就足夠了,但是傳遞數組需要C++17,因為std::arrayoperator[]從C++17開始才是constexpr的。

    整數劃分問題的constexpr元編程實現需要C++17標準:

    #include <iostream>
    #include <utility>
    #include <array>
    
    constexpr int MAX = 50;
    
    constexpr auto calculate_ans1()
    {
        std::array<std::array<std::array<int, MAX + 1>, MAX + 1>, MAX + 1> dp{};
        std::array<std::array<int, MAX + 1>, MAX + 1> ans1{};
        constexpr int max = MAX;
        for (int i = 1; i <= max; ++i)
            for (int j = 1; j <= max; ++j)
                dp[1][i][j] = i <= j;
        for (int cnt = 2; cnt <= max; ++cnt)
            for (int i = 1; i <= max; ++i)
                for (int j = 1; j <= max; ++j)
                {
                    auto min = std::min(i - 1, j);
                    for (int n = 1; n <= min; ++n)
                        dp[cnt][i][j] += dp[cnt - 1][i - n][n];
                }
        for (int cnt = 1; cnt <= max; ++cnt)
            for (int i = 1; i <= max; ++i)
                ans1[cnt][i] = dp[cnt][i][i];
        return ans1;
    }
    
    constexpr auto calculate_ans2()
    {
        constexpr auto ans1 = calculate_ans1();
        std::array<int, MAX + 1> ans2{};
        constexpr int max = MAX;
        for (int i = 1; i <= max; ++i)
            for (int cnt = 1; cnt <= i; ++cnt)
            {
                int j = i - cnt * (cnt - 1) / 2;
                if (j <= 0)
                    break;
                ans2[i] += ans1[cnt][j];
            }
        return ans2;
    }
    
    int main()
    {
        constexpr auto ans1 = calculate_ans1();
        constexpr auto ans2 = calculate_ans2();
    
        for (int cnt = 1; cnt <= 10; ++cnt)
        {
            for (int i = 1; i <= 10; ++i)
                std::cout << ans1[cnt][i] << ' ';+
            std::cout << std::endl;
        }
        std::cout << std::endl;
        for (int i = 1; i <= 50; ++i)
            std::cout << ans2[i] << ' ';
        std::cout << std::endl;
    
        int num, k;
        while (std::cin >> num)
        {
            std::cin >> k;
            std::cout << ans1[k][num] << std::endl;
            std::cout << ans2[num] << std::endl;
            std::cout << ans2[num] << std::endl;
        }
    }
    

    模板式值元編程

    模板式與C++11中的constexpr式類似,必須把循環化為遞歸。事實上C++模板是一門函數式編程語言,對值元編程和類型元編程都是如此。

    程序控制流有三種基本結構:順序、分支與循環。

    順序

    在函數式編程中,數據都是不可變的,函數總是接受若干參數,返回若干結果,參數和結果是不同的變量;修改原來的變量是不允許的。對於C++模板這門語言,函數是類模板,也稱“元函數”(metafunction);參數是模板參數;運算結果是模板類中定義的靜態編譯期常量(在C++11以前,常用enum來定義;C++11開始用constexpr)。

    比如,對於參數 \(x\),計算 \(x + 1\)\(x ^ 2\) 的元函數:

    template<int X>
    struct PlusOne
    {
        static constexpr int value = X + 1;
    };
    
    template<int X>
    struct Square
    {
        static constexpr int value = X * X;
    };
    

    這裏假定運算數的類型為int。從C++17開始,可以用auto聲明非類型模板參數。

    順序結構,是對數據依次進行多個操作,可以用函數嵌套來實現:

    std::cout << PlusOne<1>::value << std::endl;
    std::cout << Square<2>::value << std::endl;
    std::cout << Square<PlusOne<3>::value>::value << std::endl;
    std::cout << PlusOne<Square<4>::value>::value << std::endl;
    

    或者藉助constexpr函數,回歸熟悉的過程式範式:

    template<int X>
    struct SquareAndIncrease
    {
        static constexpr int calculate()
        {
            int x = X;
            x = x * x;
            x = x + 1;
            return x;
        }
        static constexpr int value = calculate();
    };
    
    void f()
    {
        std::cout << SquareAndIncrease<5>::value << std::endl;
    }
    

    過程式方法同樣可以用於分支和循環結構,以下省略;函數式方法可以相似地用於值元編程與類型元編程,所以我更青睞(主要還是逼格更高)。

    分支

    C++模板元編程實現分支的方式是模板特化與模板參數匹配,用一個額外的帶默認值的bool類型模板參數作匹配規則,特化falsetrue的情形,另一種情形留給主模板。

    比如,計算 \(x\) 的絕對值:

    template<int X, bool Pos = (X > 0)>
    struct AbsoluteHelper
    {
        static constexpr int value = X;
    };
    
    template<int X>
    struct AbsoluteHelper<X, false>
    {
        static constexpr int value = -X;
    };
    

    如果你怕用戶瞎寫模板參數,可以再包裝一層:

    template<int X>
    struct Absolute : AbsoluteHelper<X> { };
    
    void g()
    {
        std::cout << Absolute<6>::value << std::endl;
        std::cout << Absolute<-7>::value << std::endl;
    }
    

    標準庫提供了std::conditional及其輔助類型std::conditional_t用於模板分支:

    template<bool B, class T, class F>
    struct conditional;
    

    定義了成員類型type,當B == true時為T,否則為F

    模板匹配實際上是在處理switch-case的分支,bool只是其中一種簡單情況。對於對應關係不太規則的分支語句,可以用一個constexpr函數把參數映射到一個整數或枚舉上:

    enum class Port_t
    {
        PortB, PortC, PortD, PortError,
    };
    
    constexpr Port_t portMap(int pin)
    {
        Port_t result = Port_t::PortError;
        if (pin < 0)
            ;
        else if (pin < 8)
            result = Port_t::PortD;
        else if (pin < 14)
            result = Port_t::PortB;
        else if (pin < 20)
            result = Port_t::PortC;
        return result;
    }
    
    template<int Pin, Port_t Port = portMap(Pin)>
    struct PinOperation;
    
    template<int Pin>
    struct PinOperation<Pin, Port_t::PortB> { /* ... */ };
    
    template<int Pin>
    struct PinOperation<Pin, Port_t::PortC> { /* ... */ };
    
    template<int Pin>
    struct PinOperation<Pin, Port_t::PortD> { /* ... */ };
    

    如果同一個模板有兩個參數分別處理兩種分支(這已經從分支上升到模式匹配了),或同時處理分支和循環的特化,總之有兩個或以上維度的特化,需要注意兩個維度的特化是否會同時滿足,如果有這樣的情形但沒有提供兩參數都特化的模板特化,編譯會出錯。見problem2::Accumulator,它不需要提供兩個參數同時特化的版本。

    循環

    如前所述,循環要化為遞歸,循環的開始與結束是遞歸的起始與終點或兩者對調,遞歸終點的模板需要特化。比如,還是計算階乘:

    template<int N>
    struct Factorial
    {
        static constexpr int value = N * Factorial<N - 1>::value;
    };
    
    template<>
    struct Factorial<0>
    {
        static constexpr int value = 1;
    };
    

    或許階乘的遞歸定義很大程度上來源於數學,那就再看一個平方和的例子:

    template<int N>
    struct SquareSum
    {
        static constexpr int value = SquareSum<N - 1>::value + N * N;
    };
    
    template<>
    struct SquareSum<0>
    {
        static constexpr int value = 0;
    };
    

    \(1^2 + 2^2 + \cdots + n^2 = \frac {n \left( n + 1 \right) \left( 2n + 1\right)} {6}\)

    好吧,還是挺數學的,去下面看實例感覺一下吧,那裡還有break——哦不,被我放到思考題中去了。

    加群是交換群,求和順序不影響結果,上面這樣的順序寫起來方便。有些運算符不滿足交換律,需要逆轉順序。還以平方和為例:

    template<int N, int Cur = 0>
    struct SquareSumR
    {
        static constexpr int value = Cur * Cur + SquareSumR<N, Cur + 1>::value;
    };
    
    template<int N>
    struct SquareSumR<N, N>
    {
        static constexpr int value = N * N;
    };
    

    遞歸

    遞歸在過程式中是一種高級的結構,它可以直接轉化為函數式的遞歸,後面會提到兩者的異同。

    比如,計算平方根,這個例子來源於C++ Templates: The Complete Guide 2e:

    // primary template for main recursive step
    template<int N, int LO = 1, int HI = N>
    struct Sqrt {
        // compute the midpoint, rounded up
        static constexpr auto mid = (LO + HI + 1) / 2;
        // search a not too large value in a halved interval
        using SubT = std::conditional_t<(N < mid * mid),
                                       Sqrt<N, LO, mid - 1>,
                                       Sqrt<N, mid, HI>>;
        static constexpr auto value = SubT::value;
    };
    // partial specialization for end of recursion criterion
    template<int N, int S>
    struct Sqrt<N, S, S> {
        static constexpr auto value = S;
    };
    

    這個遞歸很容易化為循環,有助於你對循環化遞歸的理解。

    存儲

    實際應用中我們可能不需要把所有計算出來的值存儲起來,但在打表的題目中需要。存儲一系列數據需要用循環,循環的實現方式依然是遞歸。比如,存儲階乘(Factorial類模板見上):

    template<int N>
    inline void storeFactorial(int* dst)
    {
        storeFactorial<N - 1>(dst);
        dst[N] = Factorial<N>::value;
    }
    
    template<>
    inline void storeFactorial<-1>(int* dst)
    {
        ;
    }
    
    void h()
    {
        constexpr int MAX = 10;
        int factorial[MAX + 1];
        storeFactorial<MAX>(factorial);
        for (int i = 0; i <= MAX; ++i)
            std::cout << factorial[i] << ' ';
        std::cout << std::endl;
    }
    

    多維數組同理,例子見下方。注意,函數模板不能偏特化,但有靜態方法的類模板可以,這個靜態方法就充當原來的模板函數。

    雖然我們是對數組中的元素挨個賦值的,但編譯器的生成代碼不會這麼做,即使不能優化成所有數據一起用memcpy,至少能做到一段一段拷貝。

    類內定義的函數隱式成為inline,手動寫上inline沒有語法上的意義,但是對於一些編譯器,寫上以後函數被內聯的可能性更高,所以寫inline是一個好習慣。

    解答

    #include <iostream>
    #include <algorithm>
    
    constexpr int MAX = 50;
    
    namespace problem1
    {
    
    template<int Count, int Num, int Max>
    struct Partition;
    
    template<int Count, int Num, int Loop>
    struct Accumulator
    {
        static constexpr int value = Accumulator<Count, Num, Loop - 1>::value + Partition<Count, Num - Loop, Loop>::value;
    };
    
    template<int Count, int Num>
    struct Accumulator<Count, Num, 0>
    {
        static constexpr int value = 0;
    };
    
    template<int Count, int Num, int Max = Num>
    struct Partition
    {
        static constexpr int value = Accumulator<Count - 1, Num, std::min(Num - 1, Max)>::value;
    };
    
    template<int Num, int Max>
    struct Partition<1, Num, Max>
    {
        static constexpr int value = Num <= Max;
    };
    
    template<int Count, int Num>
    struct Store
    {
        static inline void store(int* dst)
        {
            Store<Count, Num - 1>::store(dst);
            dst[Num] = Partition<Count, Num>::value;
        }
    };
    
    template<int Count>
    struct Store<Count, 0>
    {
        static inline void store(int* dst)
        {
            ;
        }
    };
    
    template<int Count>
    inline void store(int (*dst)[MAX + 1])
    {
        store<Count - 1>(dst);
        Store<Count, MAX>::store(dst[Count]);
    }
    
    template<>
    inline void store<0>(int (*dst)[MAX + 1])
    {
        ;
    }
    
    inline void store(int(*dst)[MAX + 1])
    {
        store<MAX>(dst);
    }
    
    }
    
    namespace problem2
    {
    
    template<int Num, int Count = Num, int Helper = Num - Count * (Count - 1) / 2, bool Valid = (Helper > 0)>
    struct Accumulator
    {
        static constexpr int value = Accumulator<Num, Count - 1>::value + problem1::Partition<Count, Helper>::value;
    };
    
    template<int Num, int Count, int Helper>
    struct Accumulator<Num, Count, Helper, false>
    {
        static constexpr int value = Accumulator<Num, Count - 1>::value;
    };
    
    template<int Num, int Helper, bool Valid>
    struct Accumulator<Num, 0, Helper, Valid>
    {
        static constexpr int value = 0;
    };
    
    template<int Num>
    inline void store(int* dst)
    {
        store<Num - 1>(dst);
        dst[Num] = Accumulator<Num>::value;
    }
    
    template<>
    inline void store<0>(int* dst)
    {
        ;
    }
    
    inline void store(int* dst)
    {
        store<MAX>(dst);
    }
    
    }
    
    int ans1[MAX + 1][MAX + 1];
    int ans2[MAX + 1];
    
    int main()
    {
        problem1::store(ans1);
        problem2::store(ans2);
        int num, k;
        while (std::cin >> num)
        {
            std::cin >> k;
            std::cout << ans1[k][num] << std::endl;
            std::cout << ans2[num] << std::endl;
            std::cout << ans2[num] << std::endl;
        }
    }
    

    請對照運行時版本自行理解。

    討論

    constexpr

    constexpr不保證計算在編譯期完成,大部分編譯器在Debug模式下把所有可以推遲的constexpr計算都推遲到運行時完成。但constexpr可以作為一個強有力的優化提示,原本在最高優化等級都不會編譯期計算的代碼,在有了constexpr后編譯器會儘力幫你計算。如果編譯器實在做不到,根據你是否強制編譯期求值,編譯器會給出錯誤或推遲到運行時計算。在不同的編譯器中,這類行為的表現是不同的——眾所周知MSVC對constexpr的支持不好。

    目前(C++17)沒有任何方法可以檢查一個表達式是否是編譯期求值的,但是有方法可以讓編譯器對於非編譯期求值表達式給出一個錯誤,把期望constexpr的表達式放入模板參數或static_assert表達式都是可行的:如果編譯期求值,則編譯通過;否則編譯錯誤。

    (C++20:constevalis_constant_evaluated

    模板

    如果我們把Sqrt中的遞歸替換為如下語句:

    static constexpr auto value = (N < mid * mid) ? Sqrt<N, LO, mid - 1>::value
                                                  : Sqrt<N, mid, HI>::value;
    

    顯然計算結果是相同的,看上去還更簡潔。但是問題在於,編譯器會把Sqrt<N, LO, mid - 1>Sqrt<N, mid, HI>兩個類都實例化出來,儘管只有一個模板類的value會被使用到。這些類模板實例繼續導致其他實例產生,最終將產生 \(O \left( n \log n \right)\) 個實例。相比之下,把兩個類型名字傳給std::conditional並不會導致類模板被實例化,std::conditional只是定義一個類型別名,對該類型求::value才會實例化它,一共產生 \(O \left( \log n \right)\) 個實例。

    還有一個很常見的工具是變參模板,我沒有介紹是因為暫時沒有用到,而且我怕寫出非多項式複雜度的元程序。如果我還有機會寫一篇類型元編程的話,肯定會包含在其中的。

    函數式

    循環的一次迭代往往需要上一次迭代的結果,對應地在遞歸中就是函數對一個參數的結果依賴於對其他 \(n\) 個參數的結果。有些問題用遞歸解決比較直觀,但是如果 \(n \geq 2\),計算過程就會指數爆炸,比如:

    int fibonacci(int n)
    {
        if (n <= 2)
            return 1;
        else
            return fibonacci(n - 2) + fibonacci(n - 1);
    }
    

    計算fibonacci(30)已經需要一點點時間了,而計算fibonacci(46)(4字節帶符號整型能容納的最大斐波那契數)就很慢了。把這種遞歸轉化為循環,就是設計一個動態規劃算法的過程。然而函數式中的遞歸與過程式中的循環可能有相同的漸近複雜度:

    template<int N>
    struct Fibonacci
    {
        static constexpr int value = Fibonacci<N - 2>::value + Fibonacci<N - 1>::value;
    };
    
    template<>
    struct Fibonacci<1>
    {
        static constexpr int value = 1;
    };
    
    template<>
    struct Fibonacci<2>
    {
        static constexpr int value = 1;
    };
    

    因為只有Fibonacci<1>Fibonacci<46>這46個類模板被實例化,是 \(O \left( n \right)\) 複雜度的。

    在題目中,由於表中的所有數據都有可能用到,並且運行時不能執行計算,所以要把所有數據都計算出來。實際問題中可能只需要其中一個值,比如我現在就想知道不同整數的劃分問題對 \(50\) 的答案是多少,就寫:

    std::cout << problem2::Accumulator<50>::value << std::endl;
    

    那麼problem1::PartitionCount參數就不會超過10,不信的話你可以加一句static_assert。實例化的模板數量一共只有2000多個,而在完整的問題中這個數量要翻100倍不止。這種性質稱為惰性求值,即用到了才求值。惰性求值是必需的,總不能窮盡模板參數的所有可能組合一一實例化出來吧?

    函數式編程語言可以在運行時實現這些特性。

    性能

    我愧對這個小標題,因為C++值元編程根本沒有性能,時間和空間都是。類型元編程也許是必需,至於值元編程,emm,做點簡單的計算就可以了,這整篇文章都是反面教材。

    思考題2用GCC編譯,大概需要10分鐘;用MSVC編譯,出現我聞所未聞的錯誤:

    因為編譯器是32位的,4GB內存用完了就爆了。

    停機問題

    一個很有趣的問題是編譯器對於死循環的行為。根據圖靈停機問題,編譯器無法判斷它要編譯的元程序是否包含死循環,那麼它在遇到死循環時會怎樣表現呢?當然不能跟着元程序一起死循環,constexpr的循環次數與模板的嵌套深度都是有限制的。在GCC中,可以用-fconstexpr-depth-fconstexpr-loop-limit-ftemplate-depth等命令行參數來控制。

    思考題

    1. problem2::AccumulatorCount == 0Count == Num都要實例化,但其實只需實例化到 \(O \left( \sqrt{n} \right)\) 就可以了,試改寫之。

    2. 洛谷 NOIp2016提高組D2T1 組合數問題,用元編程實現。

      • 只需完成 \(n \leq 100, m \leq 100\) 的任務點;

      • 使用64位編譯器(指編譯器本身而非目標代碼),給編譯器億點點時間;

      • 不要去網站上提交,我已經試過了,編譯錯誤。

      • 測試數據下載。

    題目描述

    組合數 \(\binom {n} {m}\) 表示的是從 \(n\) 個物品中選出 \(m\) 個物品的方法數。舉個例子,從 \(\left( 1, 2, 3 \right)\) 三個物品中選擇兩個物品可以有 \(\left( 1, 2 \right), \left( 1, 3 \right), \left( 2, 3 \right)\) 這三種選擇方法。根據組合數的定義,我們可以給出計算組合數 \(\binom {n} {m}\) 的一般公式

    \[\binom {n} {m} = \frac {n!} {m! \left( n-m \right) !} \,, \]

    其中 \(n! = 1 \times 2 \times \cdots \times n\);特別地,定義 \(0! = 1\)

    小蔥想知道如果給定 \(n\)\(m\)\(k\),對於所有的 \(0 \leq i \leq n, 0 \leq j \leq \min \left( i, m \right)\) 有多少對 \(\left( i, j \right)\) 滿足 \(k \mid \binom {i} {j}\)

    輸入格式

    第一行有個兩個整數 \(t, k\),其中 \(t\) 代表該測試點總共有多少組測試數據,\(k\) 的意義見問題描述。

    接下來 \(t\) 行每行兩個整數 \(n, m\),其中 \(n, m\) 的意義見問題描述。

    輸出格式

    \(t\) 行,每行一個整數代表所有的 \(0 \leq i \leq n, 0 \leq j \leq \min \left( i, m \right)\) 有多少對 \(\left( i, j \right)\) 滿足 \(k \mid \binom {i} {j}\)

    輸入輸出樣例

    【輸入#1】

    1 2
    3 3
    

    【輸出#1】

    1
    

    【輸入#2】

    2 5
    4 5
    6 7
    

    【輸出#2】

    0 7
    

    說明/提示

    【樣例1說明】

    在所有可能的情況中,只有 \(\binom {2} {1} = 2\) 一種情況是 \(2\) 的倍數。

    【子任務】

    測試點 \(n\) \(m\) \(k\) \(t\)
    1 \(\leq 3\) $ \leq 3$ \(= 2\) $ = 1$
    2 \(= 3\) \(\leq 10^4\)
    3 \(\leq 7\) $ \leq 7$ \(= 4\) $ = 1$
    4 \(= 5\) \(\leq 10^4\)
    5 \(\leq 10\) $ \leq 10$ \(= 6\) $ = 1$
    6 \(= 7\) \(\leq 10^4\)
    7 \(\leq 20\) $ \leq 100$ \(= 8\) $ = 1$
    8 \(= 9\) \(\leq 10^4\)
    9 \(\leq 25\) $ \leq 2000$ \(=10\) $ = 1$
    10 \(=11\) \(\leq 10^4\)
    11 \(\leq 60\) $ \leq 20$ \(=12\) $ = 1$
    12 \(=13\) \(\leq 10^4\)
    13 \(\leq 100\) $ \leq 25$ \(=14\) $ = 1$
    14 \(=15\) \(\leq 10^4\)
    15 $ \leq 60$ \(=16\) $ = 1$
    16 \(=17\) \(\leq 10^4\)
    17 \(\leq 2000\) $ \leq 100$ \(=18\) $ = 1$
    18 \(=19\) \(\leq 10^4\)
    19 $ \leq 2000$ \(=20\) $ = 1$
    20 \(=21\) \(\leq 10^4\)
    • 對於全部的測試點,保證 \(0 \leq n, m \leq 2 \times 10^3, 1 \leq t \leq 10^4\)

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

    【其他文章推薦】

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

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

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

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

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