分類: 3C資訊

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

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

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

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

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

    【其他文章推薦】

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

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

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

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

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

  • 好開、省油還有科技感,教授把這輛車推薦給挑剔的朋友們

    好開、省油還有科技感,教授把這輛車推薦給挑剔的朋友們

    聽了的介紹,三位老同學都下定決心要買17款新蒙迪歐了,我想既然他們都將是17款新蒙迪歐的車主了,所以約了他們提車三個月之後回來探討一些用車的經驗。三個月後,和三位老同學再次相聚,討論一下蒙迪歐的用車心得。智行第一位同學,也就是那位駕齡不長的女司機,她說讓她印象最深刻的還是17款新蒙迪歐的LKA車道保持輔助系統,一旦車輛在無意識情況下偏離車道,系統將通過震動方向盤和儀錶盤信息提醒,並輔助她重回當前行駛車道,這讓她在高速公路上行駛時避免了偏離車道所造成的危險。

    身邊的朋友知道喜歡玩車

    於是經常能收到他們的諮詢

    什麼十萬買啥車、二十萬買啥車

    諸如此類的問題

    其實都可以輕鬆應對的

    然而就像廣東話說的

    “上得山多終遇虎”

    也會有天被問題難住

    三個月前的同學聚會

    有三位老同學說他們感情好

    想買同一輛車,都要二十萬出頭而且夠大氣的

    首先女同學是一名女司機

    作為職業女性的她,要經常跑高速

    而且她還是新手

    所以她要我推薦一輛好開、安全的車給她

    第二位同學則是環保主義者

    什麼都不管,就要油耗低

    (大哥,外觀大氣的車好難找油耗低的吧)

    第三位同學是一名技術宅

    典型的IT男

    需要一輛有技術含量的車子

    聽了他們的要求后,第一時間是吐槽根本沒有這樣的車。但兢兢業業鍥而不舍的我在網上做了大量的搜索之後,發現有一輛車非常適合這三位老同學,那就是長安福特的17款新蒙迪歐。

    17款新蒙迪歐指導價17.98萬起,首先在價格上是符合要求的,然後在外觀上,馬丁式的家族前臉、LED大燈、2850mm的長軸距、以及肌肉感十足的車身線條,時尚個性又不失沉穩的外觀能讓不同年齡層的人接受。

    內飾方面,採用了對稱式的布局,整體的內飾簡潔明了,多媒體系統也已經升級到第三代的SYNC操作系統,操作更加流暢,功能也更加完善。除此以外,旋鈕式換擋的設計也讓車內的檔次感提升不少。

    行駛方面,拿2.0T車型來說,最大馬力達到245ps,搭配調校成熟的6AT手自一體變速箱,動力表現充沛且平順,絲毫不用擔心有小馬拉大車的感覺;底盤方面採用了多連桿獨立后懸架,整體偏向於舒適的調校,對細碎顛簸的過濾十分徹底。可以看出,實際的行駛表現也很符合蒙迪歐的定位。

    聽了的介紹,三位老同學都下定決心要買17款新蒙迪歐了,我想既然他們都將是17款新蒙迪歐的車主了,所以約了他們提車三個月之後回來探討一些用車的經驗。

    三個月後,和三位老同學再次相聚,討論一下蒙迪歐的用車心得。

    智行

    第一位同學,也就是那位駕齡不長的女司機,她說讓她印象最深刻的還是17款新蒙迪歐的LKA車道保持輔助系統,一旦車輛在無意識情況下偏離車道,系統將通過震動方向盤和儀錶盤信息提醒,並輔助她重回當前行駛車道,這讓她在高速公路上行駛時避免了偏離車道所造成的危險。

    除此以外,對於這位新手司機來說,帶行人識別功能的智能感應制動保護系統、以及只需控制油門和剎車,即可輕鬆把車停在理想位置的主動泊車輔助系統和ACC全速智能自適應巡航控制系統都讓她駕駛蒙迪歐的過程更加輕鬆。另外,車廂內的10個安全氣囊的環繞式防護也讓她駕駛的過程中更加安心。

    智擎

    至於那位作為環保主義者的老同學,關注油耗的他對17款新蒙迪歐的EcoBoost雙渦流渦輪增壓直噴發動機贊不絕口,因為先進的雙渦流渦輪增壓技術能有效緩解低速時的遲滯性,使峰值扭矩提早爆發,帶來更佳的燃油經濟性。

    而為了達到更好的節能環保效果,他選擇了Hybrid車型,17款新蒙迪歐的混合動力版本採用阿特金森循環發動機配合電動機,並搭載eCVT電控無級變速器,讓燃油與電力良好融合,高效環保,帶來煥然一新的駕駛體驗與樂趣。

    智聯

    最後一位老同學就是做IT男的技術宅,他最滿意的就是17款新蒙迪歐搭載的福特派TM互聯技術,只需要在手機中下載福特派TM應用,就可以遠程控制車輛並且通過手機來查看燃油剩餘量等車輛概況,十分方面。

    在多媒體功能方面,車主可以通過藍牙或者USB數據線將手機和車輛相連,來拓展多媒體的功能,同時,17款新蒙迪歐的多媒體系統支持智能聲控以及智能中文手寫輸入,支持多字連寫,讓人機之間的交互變得更加簡單。

    總結

    其實早就知道17款新蒙迪歐的三大亮點能夠讓我的這三位老同學滿足,“智行”讓駕駛更加輕鬆而且安全、“智擎”在提供充沛動力的同時又保證了良好的燃油經濟性、“智聯”則結合了時下流行的智能手機,既提供了用車的便利,又豐富了多媒體功能。三管齊下,相信不只是我的這三位老同學,廣大的消費者都會被17款新蒙迪歐吸引到。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

  • 不要小看這些買車細節,去4S店裝老司機全靠這幾招…

    不要小看這些買車細節,去4S店裝老司機全靠這幾招…

    總結:買車畢竟是一件大事,最怕就是花了大價錢結果掉進了別人一早挖好的陷阱里,起不來活不起,當了冤大頭還要替別人數錢,所以在我們買車的時候還是多加留意,多留個心眼,將主動權抓在自己的手上。

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

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

  • 大發現!國3汽車的PM2.5居然比國5低?

    大發現!國3汽車的PM2.5居然比國5低?

    接下來是國三的車。所以僅從pM2。5的指數上來看,老車的成績不一定差,新車的成績也不一定好,反而車況才是更重要的因素。而那些所謂的專家。其實之前其他網友測試汽車尾氣pM2。5指數得出驚人的結果。(汽車尾氣比空氣乾淨,聽着很扯,畢竟你不可能整天去吸尾氣),就有專家出來表明,汽車的排放導致的污染並非只是直接的pM排放污染,而是“二次反應”。

    一到冬季,pM指數和霧霾就成了熱門話題,這個時候汽車就會慣例被推上輿論焦點,近期國六排放標準的出台也進一步表明了相關部門的觀點—“霧霾的很大成因就是因為汽車的排放”。每次說到空氣污染提得最多的就是pM2.5指數。

    pM指數那麼多,為什麼偏偏是pM2.5?

    pM的英文全稱為particulate Matter (顆粒物),而数字2.5是指這種顆粒物的空氣動力學直徑(aerodynamic diameter)為2.5微米( pM10則是指粒徑等於、小於10微米的顆粒物)。粒徑越小、密度越低,顆粒沉降得越慢。如果再有一些外力引起空氣微小的擾動,這些細小的顆粒即使在無風的條件下,最終也很難沉降到地面上,它們會一直在空中“遊盪”,成為危害人體健康的罪魁禍首。

    而我們常說的pM2.5的濃度或其他污染氣體的濃度是指每立方米空氣中這種污染物的質量含量,它反映了空氣的污染程度,這個值越高,就代表空氣污染越嚴重。由於空氣中污染物的種類很多,如:可吸入顆粒物、二氧化硫、氮氧化物、一氧化碳及臭氧等,為了統一評估,我國的環保部門,將每一種污染物的濃度都換算成統一的空氣污染指數,然後對外發布。俗話說病從口入,那麼我們今天就從源頭-用儀器來測試一下汽車尾氣的pM2.5指數。

    在此之前我們先看下香煙的pM指數,直接爆表,所以能戒煙朋友趕緊戒煙了。

    接着測試一下國4的車。(所測汽車均為公司同事車輛)

    然後是國5的車!

    接下來是國三的車。

    所以僅從pM2.5的指數上來看,老車的成績不一定差,新車的成績也不一定好,反而車況才是更重要的因素。

    而那些所謂的專家。。。

    其實之前其他網友測試汽車尾氣pM2.5指數得出驚人的結果。。。

    (汽車尾氣比空氣乾淨,聽着很扯,畢竟你不可能整天去吸尾氣),就有專家出來表明,汽車的排放導致的污染並非只是直接的pM排放污染,而是“二次反應”。

    這樣一群吃瓜群眾就懵逼了,你是博士后,你說什麼都對,就像一直聽說F1產生的下壓力足以讓其貼在牆上跑,但是從來沒有見過,不相信這件事的人也找不到反駁的理由。

    不管汽車的排放是不是元兇(因為現在霧霾這個鍋汽車已經背定了),相關部門對新車排放的要求越來越高並沒錯,畢竟排放再少也是排放。假設之前國三標準滿分是100分,當年生產的汽車再優秀,也只能得100。

    現在國五標準出來了,直接就說滿分是150,100分不及格,也不給你重考的機會(限制進京、限制遷入等),這種把能夠達到要求的老車也一刀切那就有點想不通了,對於車迷來說最大的損失就是中國就永遠不會有老爺車文化,因為在相關部門眼中只有報廢車。

    最後通過數據計算可以知道廣州的車輛密度比北京還要高,但是空氣質量卻要好不少,所以愛吃烤鴨的小夥伴趕緊把烤鴨吃個夠,說不定哪天烤鴨也分黃標鴨!!!

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

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

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

  • 為什麼朋友說他夢想的車是艾母雞?看完本文後終於懂了

    為什麼朋友說他夢想的車是艾母雞?看完本文後終於懂了

    說到奔馳許多人最初的印象都是寬、大、貴外加後排坐着一個人肥,牙黃,地中海的老闆似乎奔馳一向跟年輕、運動、操控這些詞扯不上什麼關係所以坊間也流傳這麼一個說法——甚至有一些“自信狂人”遇上大奔時會抑制不住腎上腺

    說到奔馳

    許多人最初的印象都是

    寬、大、貴

    外加後排坐着一個

    人肥,牙黃,地中海的老闆

    似乎奔馳一向跟

    年輕、運動、操控這些詞

    扯不上什麼關係

    所以坊間也流傳這麼一個說法——

    甚至有一些“自信狂人”

    遇上大奔時

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

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

  • 我很驚訝!十幾萬的國產SUV竟有人幹得過眾泰?

    我很驚訝!十幾萬的國產SUV竟有人幹得過眾泰?

    對於我們汽車媒體來說,這是個很方面的功能。榮威RX5配備了車身穩定系統、定速巡航、一鍵啟動、電動駐車、前撞預警系統、陡坡緩降、電動後備廂、車道保持、道路限速識別、四驅系統鎖止等豐富配置。動力系統方面,RX5將搭載1。

    國內緊湊型SUV的熱度只增不減,2016年,榮威帶來了他們潛心打造,並得到馬雲的阿里技術支持的榮威RX5,主打互聯網概念。

    榮威RX5外觀走大氣沉穩范,中規中矩但又精緻的造型給人不錯的印象。前臉“展翼格柵”將兩側前大燈融貫一體,前大燈為矩陣式LED大燈,其由24顆LED光源組成,燈組內部還集成了“如意形”LED日間行車燈。矩陣式全LED大燈更顯科技感。

    一進車內最吸睛的當屬中控10.4英寸大屏。由於大屏幕的使用,一些傳統按鍵被取消,整體設計簡潔時尚,僅在屏幕下方保留了五個實體按鍵。同時RX5在內飾用料上也多處使用了軟質皮革包裹,凸顯質感。

    RX5配備了榮威和阿里聯合開發的Yun OS,功能較為強大,可以通過大數據為用戶提供個性化服務。還可以支持綁定航拍機和運動相機,旅途中拍攝的畫面可以呈現在中控的大屏幕上。對於我們汽車媒體來說,這是個很方面的功能。

    榮威RX5配備了車身穩定系統、定速巡航、一鍵啟動、電動駐車、前撞預警系統、陡坡緩降、電動後備廂、車道保持、道路限速識別、四驅系統鎖止等豐富配置。

    動力系統方面,RX5將搭載1.5T和2.0T兩款渦輪增壓發動機,其中1.5T發動機最大功率為169馬,峰值扭矩250N·m,與之匹配的是手動/TST 7速雙離合變速箱;2.0T發動機最大功率為220馬力,峰值扭矩350N·m,與之匹配的是TST 6速濕式雙離合變速箱。

    整車開起來動力表現不錯,車輛的轉向是比較精準的,底盤偏向柔軟和舒適,但是又保留了一些路感。很符合家用SUV得定位。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

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

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

  • 一網打盡枚舉操作 .net core

    本文介紹如何使用枚舉以及,如何將枚舉類型更好的應用於項目中,看完本文可以有序的將項目中的枚舉更容易的使用到每個角落。

    1,分析枚舉

     /// <summary>
        /// 性別
        /// </summary>
        public enum Gender
        {
            /// <summary>
            ////// </summary>
            
            Man = 0,
            /// <summary>
            ////// </summary>
            
            Women = 1
        }

     

    如1所示,這是一個非常普通的枚舉類,在項目中使用的話,一般都會將它作為某實體的一個屬性,這個時候問題就來了,在頁面裡邊我們是需要拿到與之相關的描述信息和對應的值作為一個下拉框或者checkbox讓用戶去選擇,同時也不可以將Disable和enable作為給用戶最終展示的信息,需要去手動去拼,於是有了如下的方式

    2,枚舉類信息完善,增加描述信息

     /// <summary>
        /// 性別
        /// </summary>
        public enum Gender
        {
            /// <summary>
            ////// </summary>
            [Description("")]
            Man = 0,
            /// <summary>
            ////// </summary>
            [Description("女")]
            Women = 1
        }

    再給枚舉增加一個擴展方法

     /// <summary>
            /// 獲取到對應枚舉的描述-沒有描述信息,返回枚舉值
            /// </summary>
            /// <param name="enum"></param>
            /// <returns></returns>
            public static string GetDescription(this Enum @enum)
            {
                Type type = @enum.GetType();
                string name = Enum.GetName(type, @enum);
                if (name == null)
                {
                    return null;
                }
                FieldInfo field = type.GetField(name);
                if (!(Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attribute))
                {
                    return name;
                }
                return attribute?.Description;
            }

     好像到這一步的時候問題可以得到解決,通過getdescroption()這類的方法可以去獲取到與枚舉相應的描述信息用於展示,但是這也僅限於在mvc模式下,通過viewbag將枚舉的類中的每一項都加到枚舉集合中返回給頁面,在頁面裡邊遍歷,如果枚舉類型很多,那麼這類型的重複邏輯就會很多,非常的心煩。

    如果可以將這些操作做一個封裝,用一個接口可以返回所有枚舉類型的相關信息,就好了,於是有了如下的做法

    1 創建一個描述枚舉的類 

     public class EnumModel
        {
            /// <summary>
            ///枚舉原始值
            /// </summary>
            public ValueType Source { get; set; }
            /// <summary>
            /// 描述信息
            /// </summary>
            public string Description { get; set; }
            /// <summary>
            /// value
            /// </summary>
            public int Value { get; set; }
        }

    2,寫一個方法通過傳遞一個枚舉類類型去得到List<EnumModel>

    public static List<EnumModel> GetEnumListModels<T>()
            {
                var model = default(T);
                FieldInfo[] fieldinfo = typeof(T).GetFields();
                List<EnumModel> result = new List<EnumModel>();
                foreach (FieldInfo field in fieldinfo)
                {
                    EnumModel enumModel = new EnumModel();
                    if (!(Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute attribute))
                    {
                        enumModel.Description = field.GetValue(model).ToString();
                    }
                    else
                    {
                        enumModel.Description = attribute.Description;
                    }
                    enumModel.Value = field.GetValue(model).GetHashCode();
                    enumModel.Source = field.GetValue(model) as ValueType;
                    if (field.GetValue(model).ToString() != "0")
                    {
                        result.Add(enumModel);
                    }
                    
                }
                return result;
            }

    3,寫一個接口,輸入枚舉的類的名稱,調用2中的方法,得到具體的返回信息

    [Route("[controller]/{name}")]
            public IActionResult GetEnumList(string name)
            {
                Assembly assembly = Assembly.Load("Ftw");
                Type t = assembly.GetType(string.Concat("Ftw.Enum.", name), throwOnError: false, ignoreCase: true);
                if (t == null)
                {
                    throw new ServiceException(string.Concat("請確保枚舉[", name, "]在 Ftw.Enum 中定義"));
                }
                Type enumhelp = typeof(EnumHelper);
                var obj = enumhelp.GetMethod("GetEnumListModels").MakeGenericMethod(t);
                return Json(obj.Invoke(t, null));
            }
    

      解釋一下:Ftw是類庫的名稱,Enum是Ftw類庫下的一個文件夾,所有的枚舉類都在Enum下邊放着,EnumHelper是 2 中方法【GetEnumListModels】所在的類,通過反射程序集得到枚舉類型,通過反射程序集將類型傳入GetEnumListModels作為 T 最後執行方法的到list.

    比如  Gender的調用,假如 GetEnumList所在的controller是EnumController,那麼調用就是通過  Enum/Gender ,對於.net core, mvc .net core api這類項目這種方式是非常有幫助的。

     

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

    【其他文章推薦】

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

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

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

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

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

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

  • 一個生物專業學生的內心獨白:我為什麼能去互聯網大廠?

    這回,考慮到近期關注了許多新朋友,並且大多都是學生黨,可能對我還不是特別熟悉。因此我決定重新把我從非科班如何通過自學(狗屎運)進入大廠的經歷分享出來,希望能夠給予一些將要面臨秋招,或者將要準備進入互聯網行業的同學一丟丟的幫助。

    早期關注我的讀者可能都約莫着記得,我是在華科讀的本碩,專業是生物醫學工程。雖說專業的的確確是帶了生物二字,但是我老實交代,實際上並不是純粹的生物技術方向上的專業。簡單科普一下:

    生物醫學工程是屬於交叉程度非常高,主要面向生物醫學領域的工程學科。我們在學校的主要課程包含硬件設計、軟件設計、生化基礎以及儀器科學等。

    所以呢,原諒本文的些許標題黨。但是說實話,由於學科高度交叉,我們實際上所習得的技能就是一團漿糊。在計算機科學方面的系統知識積累,與純生物專業相比,也不過是五十步笑百步。

    一 啟智階段

    雖說大學期間學的都不是計算機,但是我對計算機的興趣萌芽卻是十分的早。那估計都得有二十年前了,嗯誇張了點。不過應該是在小學時候了。

    我是在老家鄉下上的小學,你們可能想象不到,那個時候的鄉下小學竟然都有電腦課。不過那時候不叫電腦課,叫做“微機課”。

    微機課是幹嘛的呢?就是一群小朋友排排隊,拖鞋進到一個乾淨的房間里,然後幾個人圍着一台那種屁股又大又方的電腦,玩紙牌。沒錯,就是windows上流行多年的紙牌遊戲。

    怎麼說呢。我玩紙牌賊溜。 大概天生對這一類東西的接受能力比較強,所以上手很快,並且除了紙牌,還有空當接龍,掃雷,3D彈球。這麼說可能有點暴露年齡了。

    最早windows系統普及的這一批遊戲真的讓我對“微機”這麼個東西產生了懵懂的概念和持續的嚮往。

    二 本科階段

    之後的成長道路中,一直對計算機耍的挺溜。但是有點尷尬的是,沒人領進門。所以一直都是在娛樂,並沒有真正意義上接觸到計算機內部或者代碼編程的世界。

    初識代碼

    所以在上大學之前,基本上對編程一無所知,但是卻有一種強烈的對計算機的熱愛。不過那時對於計算機的理解僅限於【裝系統】,【裝軟件】和【拆洗主機風扇】。不要問我為什麼沒有去學計算機,要怪就只能怪考試時坐在我旁邊的那位,大概天生就得了瘋狂抖腿病的二貨。氣!

    一不小心,還進了生科院。但是緣分這種東西,真的妙不可言。這一切的開始源於一場面試。

    所在的大學是一所以工科著稱的高校,其中創業氣氛十分濃厚。學校因此有許多小有名氣的科創團隊,基本上是由老師主導,各專業學生組成的小團體。可不要小看這些小團隊,世界級程序設計大賽的獎牌獲得者經常就出於此類團隊。剛上大學的我們單純稚嫩,自然會被被這些團隊的大幅宣傳報和滿目的獎牌稱號所吸引膜拜。我也不例外。

    當時我便懷着澎湃的心情申請了一家曾多次在微軟創新杯奪得金獎的團隊。一個從鄉下來的小伙,第一次參加面試,第一次單獨和碼農小姐姐夜晚座談,第一次參加所謂的通宵測試。

    也就是在那一個晚上,開啟了我新世界的大門。

    那天晚上給我一群編程小白的任務,是模仿百度首頁,實現整個網頁的設計、布局和基本鏈接。提前給出的提示是w3c的教程網址。

    從來沒有接觸過編程的我在此之前,連編程的流程都不清楚,更不必說編譯環境、編譯語言甚至是源代碼閱讀(當然這個任務也涉及不到這些)。

    但是也就是那一個晚上,讓我真正意義上的接觸了敲代碼這個事兒。

    沒錯,我的編程起點是HTML+CSS。可惜的是,我並沒有通過那個團隊最終的面試。不過從那以後,我就開始了網站開發的自學之路。

    個人自學的堅持很大程度上基於興趣,源於在室友面前一頓裝逼后的成就感。但這就是一個生科院的學生在課後的最大樂趣所在。

    小試牛刀

    在接觸到網站開發之後,從最初的HTML+CSS到後來的HTML5+CSS3+JS+ASP,從靜態頁面的布局到動態網頁的請求。

    雖然感覺技術的成長也就是從博客的複製粘貼走向了文檔的複製粘貼。但是終於迎來了小試牛刀的機會。

    學校某大型學校組織需要做一個展示網站,朋友拉上了我和幾個人承擔了這個事情。這算是第一次真正意義上的項目開發,不過整個網站的功能不多,主要還是展示為主,後台也直接通過學校網絡平台整合就行了。所以整體下來做的事情並不多。

    迷茫困惑

    一直以來,都是自己通過博客自學,東拼西湊的建立起知識架構。但是其實技術基礎十分不牢固,不懂計算機基礎、計算機網絡,更別說編譯原理、操作系統等基本的知識。因此在很長一段時間內,覺得好像也沒有會什麼,一直對自己的能力保持懷疑。

    在這段時間內,有過跑去搞嵌入式,硬件開發,甚至是產品設計、界面設計,視頻製作。但都不一而終。

    一段迷茫,一段頹廢。一不小心就成為校園裡的學長,於是開始緊緊茫茫的尋找實驗室。

    新的方向

    好在我們專業與生科院其它專業相比,還是帶有工科氣質的。醫學影像學本身就是這個專業最為重要的一個方向。因此懷着對編程的興趣,加入了一個專門做醫學影像的實驗室。

    進入實驗室,不再是之前的漫無目的,隨處拾荒,但卻也並不會接觸到前沿新興的技術。畢竟在實驗室里,工程技術更多的只是工具。

    不過值得一提的是,在實驗室里,我接觸到了算法思想。醫學圖像本來就是圖像處理領域的一大分支,因此圖像處理算法的了解同樣也是至關重要。

    從這個時候開始,我已經不再搞網站開發那一套東西,專心使用C++寫我的算法。那時候深度學習還沒那麼熱門,大多數的圖像處理方式還是基於傳統的算法進行。雖然算法能力薄弱,但是倒也是我所求。

    同時,為了獲得數據,還接觸到Linux系統以及一些腳本,在做研究的過程中也大大增加自己的技術面。

    與此同時,為了鞏固自己的一些技術基礎,也為了督促自己的學習,我報考了計算機等級考試和國家軟考。可能對於計算機專業的學生來說,沒有什麼含金量,輕而易舉。但是對於一個非科班的學生而言,這樣的考試可以很大程度上幫助自己去重新組織零碎的知識。畢竟不是所有人都有足夠的精力去跟着計算機系的人上課。

    本科階段的經歷讓我知道自己與計算機系的差距,雖然接觸了許多東西,但是也都不夠精,甚至沒有底氣去獨立承擔一個小的開發任務。

    因此最終選擇了讀研。希望讓自己的技術都夠在某一個點上精進,能夠獨立的做出點東西來。當然最終的結果可能並不是自己當初所想,但是說到底還是得感謝在研究生期間所做出的努力。

    三 碩士階段

    我研究生的生活是從大四就開始的。因為是本校保研生,所以在大三下確定好實驗室后,基本就直接開始搬磚的苦逼生活了。當然舍友和同學在為畢業而慶祝歡快的時候,我就開始在實驗室早出晚歸。

    讀研的開始

    進入實驗室之後,剛開始一段時間也是沒有人帶。這能靠自己去多多實驗室專業方向的文章,然後學一點基本技能。更重要的其實是為了在實驗室混個臉熟。

    實驗室的研究方向主要是核醫學成像系統,整個系統的搭建涉及到核科學研究、物理數學模型的建立、电子信息的數據獲取、自動化控制設計、机械結構設計、軟件和算法處理以及醫學實驗驗證等一系列的過程。

    但對我們個人來說,主要是做某一個方向即可。當時我還是自認有一些編程基礎,於是計劃去做軟件開發或者數據處理的方向。但是最初期是沒有人帶的,所以就花三天時間自學了python3,把廖雪峰的python教程從頭到尾走了一遍。

    可惜的是,學完之後也並沒有派上用場。

    之後實驗室新項目確立,我被導師安排到了一個新項目組裡。在項目組中,主要負責的是圖像重建的部分。圖像重建是醫學影像領域非常重要的一個研究方向,目的是將影像系統採集到的多維多尺度的信息根據成像原理,依照不同算法還原成二維或三維的斷層圖像。所以本質上來說,圖像重建的重點也是在於算法設計和優化。

    方向的多變

    開始所在的項目組內,主要的工作還是基於軟件編程和算法開發,涉及到的技術棧主要還是以C++為主,然後加上一些圖型庫,如QtopenGLVTK等等。不過都是處於調用函數調包的階段,也沒有很深入的研究算法原理和實現機制。實話說其實個人提升不大。

    過了一段時間后,實驗室師兄拉我去幫他做事。師兄本身能力非常出眾,科研水平不容置疑,所以當時就過去了。之後在他手下做的事就很多變了,不只是軟件開發,還涉及到数字電路編程,PCB制板,系統仿真等。

    那個時候還來實驗室沒多久,沒有仔細的想過自己之後的就業方向,只想着能夠在研究生期間多做些成果,多發文章。因此,跟着師兄搬磚的時候也沒有過多考慮對之後的求職是否有幫助,只是想把事情做好,做出有意義的成果。

    不過幸運的是,在大四暑假前,也就是研究生入學之前。根據師兄的指導,將一個軟件開發的工作給擴充,投了一篇領域內的頂會。雖然覺得所作的工作沒什麼特別的,但是最終文章被錄還是非常開心的。也正因為這個事,之後更想着能夠多發文章多出成果。同時,師兄也比較會熬雞湯,各種說辭讓我放佛感覺到即將成為科研巨人,登上學校官網,走上人生巔峰。

    綜合能力鍛煉

    在實驗室,除了完成自己的科研任務,還有很重要的一部分,就是寫本子。也就是申請項目基金等等文字工作。

    我估計有參与過的項目申請或結題工作不下十餘項,每次都會涉及到大量文字的整理、表達、排版等工作。一般的學生對於這樣的工作都是極度排斥的,覺得很煩又沒什麼用。當然我也是這樣覺得的。

    只不過在寫了大量的本子後會發現,其實這也是綜合能力的鍛煉。我們導師常說,博士生一定要會寫項目,碩士生的話盡量寫。說明他把這也是當作一種能力的培養。在師兄手下,我也經常性的寫本子,甚至於超過了敲的代碼。那時候也沒有很明確的技術培養規劃,所以倒也覺得還行。

    獨自前行

    只不過好景不長。帶我的師兄由於過於優秀,將要離開實驗室去其它高校就職。這對當時的我來說,處境有些許尷尬。因為我的研究方向是與師兄的工作有交叉的,同時一些設備和材料也需要師兄的支持。

    實驗室本身的氛圍是以博士帶頭,碩士輔助進行項目開展的,而我的研究方向是我自己獨立出來的一個題目。說白了就是實驗室就我一個人在搞這個,但是這個方向不是導師關注的方向。

    因此,在這之後,我又回歸到了一個人做事的階段。大概是研一下開始,我就只能自己一個人去想自己的研究課題,偶爾會跟老師討論,但是也沒有很確定的工作路線。具體的工作內容也只能靠自己去思索。

    在這段期間,我寫過verilog,調過FPGA,畫過板子,畫過工程圖,當然也寫過軟件做過算法。一個人做事的話,可以說是很自由,沒有老師管,也沒有報告的壓力。只是很多時候發現有一些想法沒辦法實現,也無法對前沿領域有很深的洞察。空有一腔發文章報效實驗室的熱血,卻發現像是站在水中的浮木上,搖搖欲墜。當然這其中,個人的問題佔有很大比重。

    摸魚的日子

    不敢說自己的有多麼自律,多麼出色。可能世上大多人都是芸芸眾生,我也不過一個普通人。在自己做研究的同時,每次到臨近頂會收集文章摘要之時,我都會很积極的熬個一兩個月,將自己的想法和結果寫成一到兩篇的會議文章。我自己也大概知道其實工作沒有太大的突破點,但也是希望能夠賭一把。

    只不過兩年來,一共整理出數篇會議摘要,都沒有被同意投稿。每一次還是會有些灰心,也有些不服。會時刻回想起導師的反饋,而後想到一堆反駁的理由。只不過都沒用。

    每一次之後,都會有一段時間的消沉。不太想去實驗室,不太想看論文,在實驗室可能也就是摸摸魚罷了。很多時候覺得鬥志滿滿,又會瞬間像泄了氣的皮球,沒有精力去完成任何事。

    職業方向

    到如今,算是大四的時光,在實驗室已經呆了快四年。平心而論,沒有做過什麼突出的成果,也沒有練就什麼出色的技術。研究生活高開低走,一度以為自己是什麼科研巨星,後來僅剩的一絲熱情也在導師的「也沒看出你有什麼天分」中黯然消逝。不過還好,在這之前,我找到了自己的職業方向。

    我本身就是一個比較後知后覺的人,一方面是懶,另一方面又對自己有一種迷之自信。我真正意義上開始準備校招還是在去年六月底七月初的時候,也就是研二下學期快結束的時候吧。我們實驗室的碩士生出去基本就是兩個方向,軟件或者硬件。搞電路的基本都選擇去做硬件開發,其它的大部分都會選擇做軟件。我也沒啥可想的,雖然做過硬件,但是技術水平根本不敢出手。所以求職的方向直接就定在了軟件開發相關。

    當時還不知道應聘互聯網還要刷題,還要複習,以為上去介紹下自己,講講自己做的跟互聯網沒有半毛錢關係的項目就可以顯得很有想法的樣子。後來真正開始準備的時候才發現,自己原來還差得有點遠。

    老實說在研究生期間,主要使用C++Matlab,主要的項目就是用Qt寫了一個客戶端,裏面有網絡通信的模塊可以對數據做一些處理,並且能夠显示圖譜。用Matlab主要就是做了一些比較基礎的圖像處理和機器學習的算法。光這項目再加上非科班帶「生物」二字的專業,的確讓不清楚的人會十動然拒。

    不過其實也沒得選擇。生醫專業對口的公司都是做醫療器械,比如聯影、邁瑞等。但是其實去這些公司也是做軟件開發工程師。相對來說,互聯網公司技術好,待遇好,發展好,自然就成為大部分的選擇去向。

    說是跨行,但是其實也沒有別的更好的選擇。我們老師常說希望我們學生以後能夠在我們這個領域發光發熱。這倒是真的,國家的發展還是離不開這些能夠在某個領域深耕的人,而不是為了一昧追求熱點和高薪而忘記了初心。

    四 求職經歷

    講講我的秋招經歷吧。我的秋招雖然準備的晚,但是其實還是挺順利的。從19年8月開始接到面試,2個月內已經拿到了15+的offer,基本平均薪資都在30w+。包括抖音美團華為小米等等。那段時間真的是狀態上來了,就是佛擋殺佛,神擋殺神。

    不過剛開始的時候,由於實驗室的原因,沒有辦法出去實習。甚至由於一些原因,一直拖到暑期前才開始準備複習。

    那時已經快七月初了。急匆匆的登上各種學習網站,發現這也太多要看了的吧,還得刷題。關鍵一看面經,這都啥呀。還要手撕代碼的嗎?

    這一看當時不要緊,關鍵晚上就焦慮的快睡不着了。每天都在想應該怎樣複習,怎麼寫簡歷,沒有項目該怎麼辦。

    剛開始的時候連簡歷都不敢投,因為老覺得簡歷一過去就會安排面試。後來發現這完全就是多慮了。大概從七月初我就開始投簡歷,因為七八月是一些公司提前批招聘的階段。

    許多非科班的學生,在投遞簡歷的時候才能發現自己的無助。我在簡歷投遞初期,基本沒有任何反饋。提前批階段,許多公司都會去爭奪更優秀的簡歷候選者,對於生物專業的學生真的沒有什麼優勢。

    但是沒有關係,既然選擇了這條路,那麼就要堅持下去。投一家無人應答,那麼就投十家,投五十家。我在整個秋招階段,總共投遞過近一百家公司。許多在提前批沒有給予反饋的公司,後期大部分都有電話聯繫重新開啟面試流程。所以,就算認為自己的簡歷再不夠出色,也要相信總會有瞎了眼的HR(誤)。

    我當時一直在堅持投簡歷,只要看到的招聘信息都會去投遞。還記得第一次做測評題的時候都非常緊張,以為這就是筆試題。非常認真的拿紙筆在計算,慌的不行。

    後來直到八月初才收到第一份面試邀請,多益網絡。當時約了面試之後簡直怕死了,雖然說複習了一個月大部分的 C++ 基礎知識都看得差不多了,但是肯定是不夠的。面試的時候面試小哥全程就低頭照着題庫念,也不看我。我這邊的音頻信號也不太行,他那邊說話都聽不清楚。兩個人就在無數次的重複和確認。關鍵是面試完之後我自我感覺還非常良好。

    最終結果還是掛了,說實話打擊挺大的,感覺因此對面試產生了恐懼。不過後來試着自己跟自己講解,慢慢的也習慣了面試的感覺。

    隨後在八月底的時候,終於收穫了第一家 ihandy 的 offer。並且在進行總管面的時候,跟面試官進行了深入的交談。面完之後讓我有一種都不好意思拒絕這家公司的念頭。這也是第一次感受到了面試官的認可。

    隨後,便一發不可收拾。這個時候,已經準備了大概兩個月,基本的技術知識我都看完了,劍指 offer 上的66題以及 leetcode 上基本的題也大概刷了兩次。同時獲得了一個 offer 之後,對自己的認可度非常高,使得面試的狀態非常好。

    後來給了面試機會的公司基本都拿到了 offer。像字節跳動、美團、虎牙等等公司,面完的感覺就是基本穩了。不過可惜的是阿里簡歷面后,內推人開始說通過了,後來不知道什麼情況流程就拖到結束了。

    騰訊也一直沒有撈過我面試。感覺如果在狀態頂峰的情況下能有面試機會的話,還是很有希望的。不過也說明簡歷依然不夠出色,非科班沒有實習經歷,項目也比較水,導致AT大廠連面試機會都沒給。這也是秋招比較遺憾的地方。

    另外,記住面試過程,跟投遞簡歷一樣,一定要多面多總結。

    如果你的表達能力不好,沒有別的好的辦法,只能多練。自己在面試前問自己問題,然後用自己的話陳述出來。甚至是錄音自己聽,感覺一下面試官聽到你的回答是做何感想。

    心態要好。面試官也是人,不可能所有人都能夠絕對公平的跟你面試,所以遇到人品不好的面試官,做好自己就行了。

    要善於總結。每一次的面試都可以做好記錄,錄音或者筆記都可以。面試完之後需要多回顧,發現自己的錯誤,感受面試官對你的引導,然後下次面試注意。我一般喜歡用印象筆記記錄東西,每一次的面試記錄我都記錄在印象筆記上。電腦手機都可以看,即使是出門現場面試也不怕。

    最後就是一定要堅持下去。金九銀十,金三銀四。把握好機會,要善於規劃自己的成功。

    五 複習準備

    想進互聯網的技術崗,基本都是要提前準備的。當然某些大佬及大大佬除外。無論是校招還是社招,都需要針對自己的求職崗位進行必要的理論知識複習、項目經歷反思和算法能力訓練。只不過校招會偏向於基礎和算法能力,社招可能都會重點考察。

    除項目經歷外,複習的階段主要分為語言基礎、數據結構和算法、計算機網絡、操作系統、數據庫以及算法刷題。

    語言基礎:

    以 C++ 為主。我不喜歡看又厚又重的語言書,因此複習全程是以博客、開發文檔和實踐相結合的方式進行技術點複習。C++ 的技術點相對於其它語言來說,不算多,也不算難。技術重點的篩選可以從面經中提取而來。當然每一屆都差不多,所以找找別人總結好的資料看就行。(想要我複習資料的,可關注公眾號後台回復秋招領取)

    數據結構和算法:

    這應該是編程的基礎,重要需要了解的數據結構不出10種。花點時間弄清楚它們的原理、結構和使用方法,常用的操作也需要掌握。最難不過紅黑樹。

    這裏的算法指的是常用的算法,比如排序、遍歷,與數據結構相結合的數據操作方式。需要保證手寫才行。

    計算機網絡:

    網絡部分的內容其實可以算是最重要的,無論是前端後端都需要掌握網絡通信過程中的操作和機制。技術點可參考網絡服務器的請求和響應過程。將其中所有涉及到的協議、機制了解清楚,就可以掌握大部分了。

    操作系統:

    如果有Linux使用經歷和腳本編程基礎在面試中會很加分。對於操作系統的理解建議按照Linuxwindos系統的區分進行。

    數據庫:

    SQL基本操作必須要掌握,還包括一些關係型數據庫的基本原理和機制,內容不多,多看看就可以掌握。

    Redis同樣也是加分項,有能力的可去研究下源碼。

    算法刷題

    這一部分不多說,普通人只能勤能補拙。無論是劍指offer66題還是leetcode都可以,劍指刷兩遍,leetcode兩百題,基本沒有問題了。刷題時不要死刷,可以根據類型刷,比如鏈表操作、二叉樹操作、動態規劃等。相同類型重複做,能夠更好的培養算法思想。

    六 感想體會

    說起求職的過程,其實真要我來評價的話,估計運氣是佔了一大部分,連我這樣都行,你們也可以的。不過最終能夠獲得一些互聯網公司認可的原因,我認為主要有以下幾點原因吧:

    1.本科階段接觸互聯網行業比較早,對於這個行業有自己的見解。

    2.本科階段有考過一些計算機水平的證書,大概系統的學了一下計算機相關的基礎知識。

    3.用C++比較多,對語言基礎的理解比較好。

    4.面試狀態比較好,比較會表達自己的想法。

    5.學習能力還行,能夠在面試官的引導下找到他想問的技術點。

    6.準備過程比較有規劃,能夠快速的掌握面試的重點。

    7.人長得老實,比較容易獲得信任感。(這個你們可能學不了^_^)

    8.比較幸運。

    前天剛碩士答辯完,這两天也把學位申請的各種材料提交上去了。一不小心,七年的大學校園時光真的要結束了,兜兜轉轉感覺好像依舊是一無是處,一事無成。但是依舊希望以後能夠:

    二龍騰飛、三羊開泰、四季平安、 五福臨門、六六大順、七星高照、八方來財、九九同心、十全十美、百事亨通、千事吉祥、萬事如意

    人生無常,活在當下,且行且珍惜!

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

    【其他文章推薦】

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

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

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

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

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

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

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

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

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

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

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

    回歸樹模型

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

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

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

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

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

    實戰

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    后剪枝

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

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

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

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

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

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

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

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

    總結

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

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

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

    本文使用 mdnice 排版

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

    【其他文章推薦】

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

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

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

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

    ※回頭車貨運收費標準

  • 【C#】AutoMapper 使用手冊

    目錄

    • 1 入門例子
    • 2 註冊
      • 2.1 Profile
    • 3 配置
      • 3.1 命名約定
      • 3.2 配置可見性
      • 3.3 全局屬性/字段過濾
      • 3.4 識別前綴和後綴
      • 3.5 替換字符
    • 4 調用構造函數
    • 5 數組和列表映射
      • 5.1 處理空集合
      • 5.2 集合中的多態
    • 6 方法到屬性映射
    • 7 自定義映射
    • 8 扁平化映射
      • 8.1 IncludeMembers
    • 9 嵌套映射

    本文基於 AutoMapper 9.0.0

    AutoMapper 是一個對象-對象映射器,可以將一個對象映射到另一個對象。

    官網地址:http://automapper.org/

    官方文檔:https://docs.automapper.org/en/latest/

    1 入門例子

    public class Foo
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    
    public class FooDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    
    public void Map()
    {
        var config = new MapperConfiguration(cfg => cfg.CreateMap<Foo, FooDto>());
    
        var mapper = config.CreateMapper();
    
        Foo foo = new Foo { ID = 1, Name = "Tom" };
    
        FooDto dto = mapper.Map<FooDto>(foo);
    }
    

    2 註冊

    在使用 Map 方法之前,首先要告訴 AutoMapper 什麼類可以映射到什麼類。

    var config = new MapperConfiguration(cfg => cfg.CreateMap<Foo, FooDto>());
    

    每個 AppDomain 只能進行一次配置。這意味着放置配置代碼的最佳位置是在應用程序啟動中,例如 ASP.NET 應用程序的 Global.asax 文件。

    從 9.0 開始 Mapper.Initialize 方法就不可用了。

    2.1 Profile

    Profile 是組織映射的另一種方式。新建一個類,繼承 Profile,並在構造函數中配置映射。

    public class EmployeeProfile : Profile
    {
        public EmployeeProfile()
        {
            CreateMap<Employee, EmployeeDto>();
        }
    }
    
    var config = new MapperConfiguration(cfg =>
    {
        cfg.AddProfile<EmployeeProfile>();
    });
    

    Profile 內部的配置僅適用於 Profile 內部的映射。應用於根配置的配置適用於所有創建的映射。

    AutoMapper 也可以在指定的程序集中掃描從 Profile 繼承的類,並將其添加到配置中。

    var config = new MapperConfiguration(cfg =>
    {
        // 掃描當前程序集
        cfg.AddMaps(System.AppDomain.CurrentDomain.GetAssemblies());
        
        // 也可以傳程序集名稱(dll 名稱)
        cfg.AddMaps("LibCoreTest");
    });
    

    3 配置

    3.1 命名約定

    默認情況下,AutoMapper 基於相同的字段名映射,並且是 不區分大小寫 的。

    但有時,我們需要處理一些特殊的情況。

    • SourceMemberNamingConvention 表示源類型命名規則
    • DestinationMemberNamingConvention 表示目標類型命名規則

    LowerUnderscoreNamingConventionPascalCaseNamingConvention 是 AutoMapper 提供的兩個命名規則。前者命名是小寫並包含下劃線,後者就是帕斯卡命名規則(每個單詞的首字母大寫)。

    我的理解,如果源類型和目標類型分別採用了 蛇形命名法駝峰命名法,那麼就需要指定命名規則,使其能正確映射。

    public class Foo
    {
        public int Id { get; set; }
    
        public string MyName { get; set; }
    }
    
    public class FooDto
    {
        public int ID { get; set; }
    
        public string My_Name { get; set; }
    }
    
    public void Map()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<Foo, FooDto>();
    
            cfg.SourceMemberNamingConvention = new PascalCaseNamingConvention();
            cfg.DestinationMemberNamingConvention = new LowerUnderscoreNamingConvention();
        });
    
        var mapper = config.CreateMapper();
    
        Foo foo = new Foo { Id = 2, MyName = "Tom" };
    
        FooDto dto = mapper.Map<FooDto>(foo);
    }
    

    3.2 配置可見性

    默認情況下,AutoMapper 僅映射 public 成員,但其實它是可以映射到 private 屬性的。

    var config = new MapperConfiguration(cfg =>
    {
        cfg.ShouldMapProperty = p => p.GetMethod.IsPublic || p.SetMethod.IsPrivate;
        cfg.CreateMap<Source, Destination>();
    });
    

    需要注意的是,這裏屬性必須添加 private set,省略 set 是不行的。

    3.3 全局屬性/字段過濾

    默認情況下,AutoMapper 嘗試映射每個公共屬性/字段。以下配置將忽略字段映射。

    var config = new MapperConfiguration(cfg =>
    {
    	cfg.ShouldMapField = fi => false;
    });
    

    3.4 識別前綴和後綴

    var config = new MapperConfiguration(cfg =>
    {
        cfg.RecognizePrefixes("My");
        cfg.RecognizePostfixes("My");
    }
    

    3.5 替換字符

    var config = new MapperConfiguration(cfg =>
    {
        cfg.ReplaceMemberName("Ä", "A");
    });
    

    這功能我們基本上用不上。

    4 調用構造函數

    有些類,屬性的 set 方法是私有的。

    public class Commodity
    {
        public string Name { get; set; }
    
        public int Price { get; set; }
    }
    
    public class CommodityDto
    {
        public string Name { get; }
    
        public int Price { get; }
    
        public CommodityDto(string name, int price)
        {
            Name = name;
            Price = price * 2;
        }
    }
    

    AutoMapper 會自動找到相應的構造函數調用。如果在構造函數中對參數做一些改變的話,其改變會反應在映射結果中。如上例,映射后 Price 會乘 2。

    禁用構造函數映射:

    var config = new MapperConfiguration(cfg => cfg.DisableConstructorMapping());
    

    禁用構造函數映射的話,目標類要有一個無參構造函數。

    5 數組和列表映射

    數組和列表的映射比較簡單,僅需配置元素類型,定義簡單類型如下:

    public class Source
    {
        public int Value { get; set; }
    }
    
    public class Destination
    {
        public int Value { get; set; }
    }
    

    映射:

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Source, Destination>();
    });
    IMapper mapper = config.CreateMapper();
    
    var sources = new[]
    {
        new Source { Value = 5 },
        new Source { Value = 6 },
        new Source { Value = 7 }
    };
    
    IEnumerable<Destination> ienumerableDest = mapper.Map<Source[], IEnumerable<Destination>>(sources);
    ICollection<Destination> icollectionDest = mapper.Map<Source[], ICollection<Destination>>(sources);
    IList<Destination> ilistDest = mapper.Map<Source[], IList<Destination>>(sources);
    List<Destination> listDest = mapper.Map<Source[], List<Destination>>(sources);
    Destination[] arrayDest = mapper.Map<Source[], Destination[]>(sources);
    

    具體來說,支持的源集合類型包括:

    • IEnumerable
    • IEnumerable
    • ICollection
    • ICollection
    • IList
    • IList
    • List
    • Arrays

    映射到現有集合時,將首先清除目標集合。如果這不是你想要的,請查看AutoMapper.Collection。

    5.1 處理空集合

    映射集合屬性時,如果源值為 null,則 AutoMapper 會將目標字段映射為空集合,而不是 null。這與 Entity Framework 和 Framework Design Guidelines 的行為一致,認為 C# 引用,數組,List,Collection,Dictionary 和 IEnumerables 永遠不應該為 null

    5.2 集合中的多態

    這個官方的文檔不是很好理解。我重新舉個例子。實體類如下:

    public class Employee
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    
    public class Employee2 : Employee
    {
        public string DeptName { get; set; }
    }
    
    public class EmployeeDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    
    public class EmployeeDto2 : EmployeeDto
    {
        public string DeptName { get; set; }
    }
    

    數組映射代碼如下:

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Employee, EmployeeDto>().Include<Employee2, EmployeeDto2>();
        cfg.CreateMap<Employee2, EmployeeDto2>();
    });
    IMapper mapper = config.CreateMapper();
    
    var employees = new[]
    {
        new Employee { ID = 1, Name = "Tom" },
        new Employee2 { ID = 2, Name = "Jerry", DeptName = "R & D" }
    };
    
    var dto = mapper.Map<Employee[], EmployeeDto[]>(employees);
    

    可以看到,映射后,dto 中兩個元素的類型,一個是 EmployeeDto,一個是 EmployeeDto2,即實現了父類映射到父類,子類映射到子類。

    如果去掉 Include 方法,則映射后 dto 中兩個元素的類型均為 EmployeeDto

    6 方法到屬性映射

    AutoMapper 不僅能實現屬性到屬性映射,還可以實現方法到屬性的映射,並且不需要任何配置,方法名可以和屬性名一致,也可以帶有 Get 前綴。

    例如下例的 Employee.GetFullName() 方法,可以映射到 EmployeeDto.FullName 屬性。

    public class Employee
    {
        public int ID { get; set; }
    
        public string FirstName { get; set; }
    
        public string LastName { get; set; }
    
        public string GetFullName()
        {
            return $"{FirstName} {LastName}";
        }
    }
    
    public class EmployeeDto
    {
        public int ID { get; set; }
    
        public string FirstName { get; set; }
    
        public string LastName { get; set; }
    
        public string FullName { get; set; }
    }
    

    7 自定義映射

    當源類型與目標類型名稱不一致時,或者需要對源數據做一些轉換時,可以用自定義映射。

    public class Employee
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public DateTime JoinTime { get; set; }
    }
    
    public class EmployeeDto
    {
        public int EmployeeID { get; set; }
    
        public string EmployeeName { get; set; }
    
        public int JoinYear { get; set; }
    }
    

    如上例,IDEmployeeID 屬性名不同,JoinTimeJoinYear 不僅屬性名不同,屬性類型也不同。

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Employee, EmployeeDto>()
            .ForMember("EmployeeID", opt => opt.MapFrom(src => src.ID))
            .ForMember(dest => dest.EmployeeName, opt => opt.MapFrom(src => src.Name))
            .ForMember(dest => dest.JoinYear, opt => opt.MapFrom(src => src.JoinTime.Year));
    });
    

    8 扁平化映射

    對象-對象映射的常見用法之一是將複雜的對象模型並將其展平為更簡單的模型。

    public class Employee
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public Department Department { get; set; }
    }
    
    public class Department
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    
    public class EmployeeDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public int DepartmentID { get; set; }
    
        public string DepartmentName { get; set; }
    }
    

    如果目標類型上的屬性,與源類型的屬性、方法都對應不上,則 AutoMapper 會將目標成員名按駝峰法拆解成單個單詞,再進行匹配。例如上例中,EmployeeDto.DepartmentID 就對應到了 Employee.Department.ID

    8.1 IncludeMembers

    如果屬性命名不符合上述的規則,而是像下面這樣:

    public class Employee
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public Department Department { get; set; }
    }
    
    public class Department
    {
        public int DepartmentID { get; set; }
    
        public string DepartmentName { get; set; }
    }
    
    public class EmployeeDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public int DepartmentID { get; set; }
    
        public string DepartmentName { get; set; }
    }
    

    Department 類中的屬性名,直接跟 EmployeeDto 類中的屬性名一致,則可以使用 IncludeMembers 方法指定。

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Employee, EmployeeDto>().IncludeMembers(e => e.Department);
        cfg.CreateMap<Department, EmployeeDto>();
    });
    

    9 嵌套映射

    有時,我們可能不需要展平。看如下例子:

    public class Employee
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public int Age { get; set; }
    
        public Department Department { get; set; }
    }
    
    public class Department
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public string Heads { get; set; }
    }
    
    public class EmployeeDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    
        public DepartmentDto Department { get; set; }
    }
    
    public class DepartmentDto
    {
        public int ID { get; set; }
    
        public string Name { get; set; }
    }
    

    我們要將 Employee 映射到 EmployeeDto,並且將 Department 映射到 DepartmentDto

    var config = new MapperConfiguration(cfg =>
    {
        cfg.CreateMap<Employee, EmployeeDto>();
        cfg.CreateMap<Department, DepartmentDto>();
    });
    

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案