Windows平台LoadLibrary加載動態庫搜索路徑的問題

一、背景

在給Adobe Premiere/After Effects等後期製作軟件開發第三方插件的時候,我們總希望插件依賴的動態庫能夠脫離插件的位置,單獨存儲到另外一個地方。這樣一方面可以與其他程序共享這些動態庫,還能保證插件安裝時非常的清爽。就Adobe Premiere Pro/After Effects來說,插件文件是放到C:\Program Files\Adobe\Common\Plug-ins\7.0\MediaCore(Windows平台)的。這個是PremiereProAfterEffects的公共插件目錄,二者在啟動的時候都會嘗試去這個位置加載插件。與此同時,我們希望自己開發的插件所依賴的動態庫放到另外的位置,另外也希望插件显示鏈接的動態庫能夠盡量少。因為如果是顯式鏈接的話,這些插件依賴的動態庫必須和插件保存在同一個位置。不然插件找不到這些依賴文件就會加載失敗的。當然,我們也可以在環境變量裏面增加一條路徑,但是這容易污染環境變量,或者與其他的程序庫產生衝突。LoadLibrary在這個時候就產生作用了。LoadLibrary通過將指定路徑的動態庫加載到當前的調用進程,然後獲取其導出的函數就可以正常使用了。對於像第三方插件這樣的應用場景,LoadLibrary可以說是個不錯的實現方式。但是正因此也有個弊端,我們無法使用工具得知其的依賴庫。

二、使用實例

我們在給Adobe Premiere Pro開發的一款插件中,正是使用了這種方法:
(1)首先從註冊表中獲取到我們插件依賴的動態庫文件所在的位置:

 1 bool GetInstallationPath(std::string& result) {
 2     DWORD data_type;
 3     CHAR value[1024];
 4     PVOID pv_data = value;
 5     DWORD size = sizeof(value);
 6     auto err = RegGetValue(HKEY_CLASSES_ROOT, "test_app\\plugin", "install_location", RRF_RT_ANY, &data_type, pv_data, &size);
 7     if (err == ERROR_SUCCESS) {
 8         std::string filepath(value); 
 9         std::regex_replace(std::back_inserter(result), filepath.begin(), filepath.end(), std::regex("[\\\\/]+[^\\\\/]+$"), "");
10         return true;
11     }
12     return false;
13 }

(2)通過調用LoadLibrary來加載指定的依賴庫

std::string    dirname;
if (!GetInstallationPath(dirname)) {
    return false;
}  
SetDllDirectory(dirname.c_str());
insmedia_dll.handle = LoadLibrary("core.dll");

如上述代碼所示,我們的插件唯一依賴的動態庫叫core.dll。而core.dll文件存放的位置記錄在註冊表中。程序先從註冊表中獲取core.dll所在的文件夾,然後設置到DLL的搜索路徑中。最後再調用LoadLibrary加載它。在最初開發及發布后,插件運行的很好。然而,在Adobe發布Premiere Pro CC 2020之後,插件就不工作了。這是為啥呢?根據過往的經驗來看,插件加載不上只有一個原因:依賴的動態庫缺失或者是加載錯了版本。那麼,我們就來看看到底是哪個依賴加載錯了導致插件加載失敗呢?通過在WinDBG裏面調試看到了如下的差異:

看上圖很顯然,我們的插件在加載ffmpeg的庫文件時,先找到了PremierePro安裝根目錄裏面的版本了。而PremierePro使用的ffmpeg版本顯然跟我們不一樣。正是因為這兩個庫的版本不對,導致我們的插件加載失敗了。那麼,LoadLibrary這種方法顯然還是存在一些Bug了。我們的core.dll還依賴OpenCV、ffmpeg等第三方庫。看MSDN的解釋是,LoadLibrary會先從調用進程的目錄下搜索動態庫的依賴。這樣的行為顯然不是我們想要的。這個時候,我們還有個選擇:使用LoadLibraryEx。具體的使用方法仍然一樣,只不過傳給LoadLibraryEx的第一個參數是我們要加載的動態庫的絕對路徑:

 1 std::string    dirname;
 2 if (!GetInstallationPath(dirname)) {
 3     return false;
 4 }  
 5  
 6 std::string absolute_path = dirname + "\\InsMedia.dll";
 7 insmedia_dll.handle = LoadLibraryEx(absolute_path.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
 8 if (!insmedia_dll.handle) {
 9     return false;
10 }

注意到第三個參數為LOAD_WITH_ALTERED_SEARCH_PATH,通過指定LOAD_WITH_ALTERED_SEARCH_PATH,讓系統DLL搜索順序從DLL所在目錄開始。這樣就能夠保證加載動態庫的時候優先加載我們打包的動態庫。從而避免因為動態庫加載錯誤導致插件失敗。

從上圖可以看到,所有依賴的動態庫都變成了我們自己提供的庫文件了,插件也能正常加載了。完美!

三、參考鏈接

1. https://blog.csdn.net/cuglifangzheng/article/details/50580279
2. https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

【編程題與分析題】Javascript 之繼承的多種實現方式和優缺點總結

[!NOTE]
能熟練掌握每種繼承方式的手寫實現,並知道該繼承實現方式的優缺點。

原型鏈繼承

    function Parent() {
      this.name = 'zhangsan';
      this.children = ['A', 'B', 'C'];
    }
    Parent.prototype.getName = function() {
      console.log(this.name);
    }
    
    function Child() {
      
    }
    Child.prototype = new Parent();
    var child = new Child();
    console.log(child.getName());

[!NOTE]
主要問題:
1. 引用類型的屬性被所有實例共享(this.children.push(‘name’))
2. 在創建Child的實例的時候,不能向Parent傳參

借用構造函數(經典繼承)

    function Parent(age) {
      this.names = ['zhangsan', 'lisi'];
      this.age = age;
      
      this.getName = function() {
        return this.names;
      }
      
      this.getAge = function() {
        return this.age;
      }
    }
    
    function Child(age) {
      Parent.call(this, age);
    }
    var child = new Child(18);
    child.names.push('haha');
    console.log(child.names);
    
    var child2 = new Child(20);
    child2.names.push('yaya');
    console.log(child2.names);

[!NOTE]
優點:
1. 避免了引用類型的屬性被所有實例共享
2. 可以直接在Child中向Parent傳參
缺點:
方法都在構造函數中定義了,每次創建實例都會創建一遍方法

組合繼承(原型鏈繼承和經典繼承雙劍合璧)

    /**
    * 父類構造函數
    * @param name
    * @constructor
    */
    function Parent(name) {
      this.name = name;
      this.colors = ['red', 'green', 'blue'];
    }
    
    Parent.prototype.getName = function() {
      console.log(this.name);
    }
    
    // child
    function Child(name, age) {
      Parent.call(this, name);
      this.age = age;
    }
    
    Child.prototype = new Parent();
    // 校正child的構造函數
    Child.prototype.constructor = Child;
    
    // 創建實例
    var child1 = new Child('zhangsan', 18);
    child1.colors.push('orange');
    console.log(child1.name, child1.age, child1.colors);    // zhangsan 18 (4) ["red", "green", "blue", "orange"]
    
    var child2 = new Child('lisi', 28);
    console.log(child2.name, child2.age, child2.colors);    // lisi 28 (3) ["red", "green", "blue"]

[!NOTE]
優點: 融合了原型鏈繼承和構造函數的優點,是Javascript中最常用的繼承模式

—— 高級繼承的實現

原型式繼承

    function createObj(o) {
      function F(){};
      // 關鍵:將傳入的對象作為創建對象的原型
      F.prototype = o;
      return new F();
    }
    
    // test
    var person = {
        name: 'zhangsan',
        friends: ['lisi', 'wangwu']
    }
    var person1 = createObj(person);
    var person2 = createObj(person);
    
    person1.name = 'wangdachui';
    console.log(person1.name, person2.name);  // wangdachui, zhangsan
    
    person1.friends.push('songxiaobao');
    console.log(person2.friends);       // lisi wangwu songxiaobao

[!WARNING]
缺點:
對於引用類型的屬性值始終都會共享相應的值,和原型鏈繼承一樣

寄生式繼承

    // 創建一個用於封裝繼承過程的函數,這個函數在內部以某種形式來增強對象
    function createObj(o) {
      var clone = Object.create(o);
      clone.sayName = function() {
        console.log('say HelloWorld');
      }
      return clone;
    }

[!WARNING]
缺點:與借用構造函數模式一樣,每次創建對象都會創建一遍方法

寄生組合式繼承

基礎版本

    function Parent(name) {
      this.name = name;
      this.colors = ['red', 'green', 'blue'];
    }
    
    Parent.prototype.getName = function() {
      console.log(this, name);
    }
    
    function Child(name, age) {
      Parent.call(this, name);
      this.age = age;
    }
    
    // test1:
    // 1. 設置子類實例的時候會調用父類的構造函數
    Child.prototype = new Parent();
    // 2. 創建子類實例的時候也會調用父類的構造函數
    var child1 = new Child('zhangsan', 18);   // Parent.call(this, name);
    
    
    // 思考:如何減少父類構造函數的調用次數呢?
    var F = function(){};
    F.prototype = Parent.prototype;
    Child.prototype = new F();
    
    // 思考:下面的這一句話可以嗎?
    /* 分析:因為此時Child.prototype和Parent.prototype此時指向的是同一個對象,
            因此部分數據相當於此時是共享的(引用)。
            比如此時增加 Child.prototype.testProp = 1; 
            同時會影響 Parent.prototype 的屬性的。
          如果不模擬,直接上 es5 的話應該是下面這樣吧
          Child.prototype = Object.create(Parent.prototype);*/
    Child.prototype = Parent.prototype;
    
    // 上面的三句話可以簡化為下面的一句話
    Child.prototype = Object.create(Parent.prototype);
    
    
    
    // test2:
    var child2 = new Child('lisi', 24);

優化版本

    // 自封裝一個繼承的方法
    function object(o) {
      // 下面的三句話實際上就是類似於:var o = Object.create(o.prototype)
      function F(){};
      F.prototype = o.prototype;
      return new F();
    }
    
    function prototype(child, parent) {
      var prototype = object(parent.prototype);
      // 維護原型對象prototype裏面的constructor屬性
      prototype.constructor = child;
      child.prototype = prototype;
    }
    
    // 調用的時候
    prototype(Child, Parent)

創建對象的方法

  • 字面量創建
  • 構造函數創建
  • Object.create()
var o1 = {name: 'value'};
var o2 = new Object({name: 'value'});

var M = function() {this.name = 'o3'};
var o3 = new M();

var P = {name: 'o4'};
var o4 = Object.create(P)

原型

  • JavaScript 的所有對象中都包含了一個 __proto__ 內部屬性,這個屬性所對應的就是該對象的原型
  • JavaScript 的函數對象,除了原型 __proto__ 之外,還預置了 prototype 屬性
  • 當函數對象作為構造函數創建實例時,該 prototype 屬性值將被作為實例對象的原型 __proto__

原型鏈

任何一個實例對象通過原型鏈可以找到它對應的原型對象,原型對象上面!

的實例和方法都是實例所共享的。

一個對象在查找以一個方法或屬性時,他會先在自己的對象上去找,找不到時,他會沿着原型鏈依次向上查找。

注意: 函數才有prototype,實例對象只有有__proto__, 而函數有的__proto__是因為函數是Function的實例對象

instanceof原理

判斷實例對象的__proto__屬性與構造函數的prototype是不是用一個引用。如果不是,他會沿着對象的__proto__向上查找的,直到頂端Object。

判斷對象是哪個類的直接實例

使用對象.construcor直接可判斷

構造函數,new時發生了什麼?

   var obj  = {}; 
   obj.__proto__ = Base.prototype;
   Base.call(obj);  
  1. 創建一個新的對象 obj;
  2. 將這個空對象的__proto__成員指向了Base函數對象prototype成員對象
  3. Base函數對象的this指針替換成obj, 相當於執行了Base.call(obj);
  4. 如果構造函數显示的返回一個對象,那麼則這個實例為這個返回的對象。 否則返回這個新創建的對象

類的聲明

// 普通寫法
function Animal() {
  this.name = 'name'
}

// ES6
class Animal2 {
  constructor () {
    this.name = 'name';
  }
}

繼承

借用構造函數法

在構造函數中 使用Parent.call(this)的方法繼承父類屬性。

原理: 將子類的this使用父類的構造函數跑一遍

缺點: Parent原型鏈上的屬性和方法並不會被子類繼承

function Parent() {
  this.name = 'parent'
}

function Child() {
  Parent.call(this);
  this.type = 'child'
}

原型鏈實現繼承

原理:把子類的prototype(原型對象)直接設置為父類的實例

缺點:因為子類只進行一次原型更改,所以子類的所有實例保存的是同一個父類的值。
當子類對象上進行值修改時,如果是修改的原始類型的值,那麼會在實例上新建這樣一個值;
但如果是引用類型的話,他就會去修改子類上唯一一個父類實例裏面的這個引用類型,這會影響所有子類實例

function Parent() {
  this.name = 'parent'
  this.arr = [1,2,3]
}

function Child() {
  this.type = 'child'
}

Child.prototype = new Parent();
var c1 = new Child();
var c2 = new Child();
c1.__proto__ === c2.__proto__

組合繼承方式

組合構造函數中使用call繼承和原型鏈繼承。

原理: 子類構造函數中使用Parent.call(this);的方式可以繼承寫在父類構造函數中this上綁定的各屬性和方法;
使用Child.prototype = new Parent()的方式可以繼承掛在在父類原型上的各屬性和方法

缺點: 父類構造函數在子類構造函數中執行了一次,在子類綁定原型時又執行了一次

function Parent() {
  this.name = 'parent'
  this.arr = [1,2,3]
}

function Child() {
  Parent.call(this);
  this.type = 'child'
}

Child.prototype = new Parent();

組合繼承方式 優化1:

因為這時父類構造函數的方法已經被執行過了,只需要關心原型鏈上的屬性和方法了

Child.prototype = Parent.prototype;

缺點:

  • 因為原型上有一個屬性為constructor,此時直接使用父類的prototype的話那麼會導致 實例的constructor為Parent,即不能區分這個實例對象是Child的實例還是父類的實例對象。
  • 子類不可直接在prototype上添加屬性和方法,因為會影響父類的原型

注意:這個時候instanseof是可以判斷出實例為Child的實例的,因為instanceof的原理是沿着對象的__proto__判斷是否有一個原型是等於該構造函數的原型的。這裏把Child的原型直接設置為了父類的原型,那麼: 實例.__proto__ === Child.prototype === Child.prototype

組合繼承方式 優化2 – 添加中間對象【最通用版本】:

function Parent() {
  this.name = 'parent'
  this.arr = [1,2,3]
}

function Child() {
  Parent.call(this);
  this.type = 'child'
}

Child.prototype = Object.create(Parent.prototype); //提供__proto__
Child.prototype.constrctor = Child;

Object.create()方法創建一個新對象,使用現有的對象來提供新創建的對象的__proto__

創建JS對象的多種方式總結

工廠模式

 
    /**
    * 工廠模式創建對象
    * @param name
    * @return {Object}
    */
    function createPerson(name){
        var o = new Object();
        o.name = name;
        o.getName = function() {
          console.log(this.name);
        }
        return o;
    }
    var person = createPerson('zhangsan');
    console.log(person.__proto__ === Object.prototype); // true

缺點:無法識別當前的對象,因為創建的所有對象實例都指向的是同一個原型

構造函數模式

構造函數創建對象基礎版本

    /**
    * 使用構造函數的方式來創建對象
    * @param name
    * @constructor
    */
    function Person(name) {
      this.name = name;
      this.getName = function() {
        console.log(this.name)
      }
    }
    var person = new Person('lisi');
    console.log(person.__proto__ === Person.prototype)

優點:實例剋識別偽一個特定的類型
缺點:每次創建實例對象的時候,每個方法都會被創建一次

構造函數模式優化

    function Person(name) {
      this.name = name;
      this.getName = getName;
    }
    
    function getName() {
      console.log(this.name);
    }
    
    var person = new Person('zhangsan');
    console.log(person.__proto__ === Person.prototype);

優點:解決了每個方法都要被重新創建的問題
缺點:不合乎代碼規範……

原型模式

原型模式基礎版

    function Person(name) {
      
    }
    Person.prototype.name = 'lisi';
    Person.prototype.getName = function() {
      console.log(this.name);
    }
    var person = new Person();
    console.log(Person.prototype.constructor)       // Person

優點:方法不會被重新創建
缺點:1. 所有的屬性和方法所有的實例上面都是共享的;2. 不能初始化參數

原型模式優化版本一

    function Person(name) {
      
    }
    Person.prototype = {
        name: 'lisi',
        getName: function() {
          console.log(this.name);
        }
    }
    var person = new Person();
    console.log(Person.prototype.constructor)       // Object
    console.log(person.constructor == person.__proto__.constructor) // true

優點:封裝性好了一些
缺點:重寫了Person的原型prototype屬性,丟失了原始的prototype上的constructor屬性

原型模式優化版本二

    function Person(name) {
      
    }
    Person.prototype = {
        constructor: Person,
        name: 'lisi',
        getName: function() {
          console.log(this.name)
        }
    }
    var person = new Person();

優點:實例可以通過constructor屬性找到所屬的構造函數
缺點:所有的屬性和方法都共享,而且不能初始化參數

組合模式

    function Person(name) {
      this.name = name;
    }
    Person.prototype = {
        constructor: Person,
        getName: function() {
          console.log(this.name)
        }
    }
    var person = new Person('zhangsan');

優點:基本符合預期,屬性私有,方法共享,是目前使用最廣泛的方式
缺點:方法和屬性沒有寫在一起,封裝性不是太好

動態原型模式

    // 第一種創建思路:
    function Person(name) {
       this.name = name;
       if (typeof this.getName !== 'function') {
           Person.prototype.getName = function() {
             console.log(this.name);
           }
       }
    }
    var person = new Person();

    // 第二種創建的思路:使用對象字面量重寫原型上的方法
    function Person(name) {
      this.name = name;
      if (typeof this.getName !== 'function') {
          Person.prototype = {
              constructor: Person,
              getName: function() {
                console.log(this.name)
              }
          }
          return new Person(name);
      }
    }
    
    var person1 = new Person('zhangsan');
    var person2 = new Person('lisi');
    console.log(person1.getName());
    console.log(person2.getName());
    

寄生構造函數模式

    /**
    * 寄生構造函數模式
    * @param name
    * @return {Object}
    * @constructor
    */
   function Person(name){
        var o = new Object();
        o.name = name;
        o.getName = function() {
          console.log(this.name)
        }
        return o;
   }
   var person = new Person('zhangsan');
   console.log(person instanceof Person);   // false
   console.log(person instanceof Object);   // true
   
   
   // 使用寄生-構造函數-模式來創建一個自定義的數組
   /**
    * 特殊數組的構造器
    * @constructor
    */
   function SpecialArray() {
     var values = new Array();
     /*for (var i = 0, len = arguments.length; i < len; i++) {
         values.push(arguments[i]);
     }*/
     // 開始添加數據(可以直接使用apply的方式來優化代碼)
     values.push.apply(values, arguments);
     
     // 新增的方法
     values.toPipedString = function(){
         return this.join('|');
     }
     
     return values;
   }
   
   // 使用new來創建對象
   var colors1 = new SpecialArray('red1', 'green1', 'blue1');
   // 不使用new來創建對象
   var colors2 = SpecialArray('red2', 'green2', 'blue2');
   
   console.log(colors1, colors1.toPipedString());
   console.log(colors2, colors2.toPipedString());

穩妥構造函數模式

    /**
    * 穩妥的創建對象的方式
    * @param name
    * @return {number}
    * @constructor
    */
    function Person(name){
        var o = new Object();
        o.sayName = function() {
           // 這裡有點類似於在一個函數裏面使用外部的變量
           // 這裏直接輸出的是name
          console.log(name);
        }
        return o;
    }
    var person =  Person('lisi');
    person.sayName();
    person.name = 'zhangsan';
    person.sayName();
    console.log(person instanceof Person);      // false
    console.log(person instanceof Object);      // false

[!NOTE]
與寄生的模式的不同點:1. 新創建的實例方法不引用this 2.不使用new操作符調用構造函數
優點:最適合一些安全的環境中使用
缺點:和工廠模式一樣,是無法識別對象的所屬類型的

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

賓士 Future Bus 無人巴士上路,會閃行人還懂得看紅綠燈

如果你從荷蘭阿姆斯特丹史基浦機場出來搭上快捷巴士(BRT),注意到巴士明明正在行進中,司機的雙手卻輕鬆擺在一旁,先別太緊張,你可能搭上了裝載賓士CityPilot 自動駕駛系統的「未來巴士(Future Bus)」!

正在阿姆斯特丹史基浦機場和哈勒姆鎮之間進行長距離測試的Future Bus,改裝自賓士的Citaro 型號巴士,以賓士220 kw 的六缸引擎驅動,最高時速達70 公里,並由CityPilot 自動駕駛系統負責將車輛保持在車道中央,方向導航以及加減速。

根據賓士Citaro 改裝自動駕駛巴士,裝有GPS 與兩組雷達系統。

當巴士穩定停下,車門打開時,出入口的冷光條會由禁止的紅色轉成通行的綠色。上車刷卡後,你可以看到司機的方向盤前方設置一個長方形螢幕,隨時顯示車速、電量以及鄰近道路交通號誌等資訊,如果車身感應到有東西靠近,也會跳出警示符號。

基於法規而存在的駕駛平時只需要坐在駕駛座上監控,特殊狀況才需要接手控車,而CityPilot 自動駕駛系統被認為能減少人為駕駛疏失,並靠著更穩定的行車方式增進交通效率和搭乘舒適度。

Future Bus 總共安裝10 台攝影機監控道路與車身四周的情況,4 個短程雷達感應器負責巴士前方50 公分至10 公尺的車況,還有兩個範圍達50 公尺的立體相機為系統提供3D 視覺與障礙物辨識。

自動駕駛巴士主要運用雷達、GPS、道路追蹤攝影機和環景相機來判定自己的位置,車底還有相機負責辨識路面坑洞狀況。

基於當地法規,雖然是自動駕駛巴士,但還是會配有一名司機。

這幾年已有不少在校園或遊樂園等封閉場域進行載客的無人巴士,與IBM 超級電腦華生合作的自動駕駛小巴Olli 也已經在華盛頓特區上路了。不過賓士Future Bus 負責的機場300 號路線巴士專用通道,行駛路段包含十字路口、行人區與隧道,因此除了為乘客自動開關車門、閃避行人等突然靠近的物體,還具備判讀交通號誌、在正確時機通過路口的能力。可說是自動駕駛公車測試的又一里程碑!

(本文由 授權提供。所有圖片來源:mercedes-benz)

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

高通、Lear簽署電動車無線充電授權協議

手機晶片公司美國高通,與汽車座椅與電子系統供應商Lear簽訂了電動汽車無線充電(wireless electric vehicle charging,WEVC)授權協議。Lear將在高通授權下,將高通的Halo WEVC技術應用於旗下產品,以支援PHEV、EV製造商與無線充電基礎設施公司推動WEVC的商業應用。高通將提供技術與工程支援。

高通與Lear正與多家車廠合作推動WEVC生產計畫。高通副總裁暨無線充電部門總經理Steve Pazol表示,Lear能研發多樣化的WEVC系統,包括多線圈、螺絲館、循環系統等,足以滿足眾多客戶的多種需求。與Lear的合作,也將推動高通Halo技術的商業化,並進一步使WEVC技術走向實用市場。

Lear將無線充電技術視為新的市場契機,並表示無線充電技術將有助客戶擬定策略、搶進現有與未來的新車市場。Lear正在向高通取得全面的技術轉讓組合,希望研發出兼具技術與商業性的WEVC系統,以支援後續各種進階款的WEVC系統設計。

(圖片來源:高通Halo技術專頁截圖)

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

特斯拉即將在台開賣,選擇台灣原因曝光

美國電動車龍頭品牌特斯拉(Tesla)正式來台設點,預計將在今年9月展開銷售服務。特斯拉之所以選擇台灣的原因近日曝光,主要是因為台灣擁有完整的供應鏈,且幅員適中之故。

《中央社》指出,台灣南北全長不到400公里,且全島幅員不算太大;而Tesla Model S每次充飽電可行駛至少260公里,使車主每次充飽電都可以直接開車往來南北,特斯拉要布建全島充電網也會相對容易。

特斯拉目前在全球已設置681個超級充電站,充飽電的時間約需30分鐘,通常設置於餐廳、商場與Wi-Fi熱點周遭,讓車主可在等待充電的同時進行日常消費。

另一方面,特斯拉許多關鍵零件,如電動馬達、電控系統等零組件都來自台灣,零件供應量比例最高達25%,類別也可達40%左右。強大的在地供應鏈,也強化了特斯拉在台拓展市場的實力。台灣與特斯拉電動車相關的業者,包括動力電池線束公司貿聯-KY、變速箱齒輪廠和大、DC/DC轉換器與車上充電系統供應商台達電、電動馬達廠富田、大電流端子廠健和興等。

最後,台灣品牌電動機車Gogoro已在台灣本地打出市場名聲。有了Gogoro的領航經驗,台灣人對電動車輛的接受度更高,也將有利特斯拉在台灣開疆拓土。

(照片來源:Tesla Taiwan臉書專頁)

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

Gogoro 騎進柏林,與Bosch子公司共推租賃服務

來自台灣的智慧雙輪交通工具品牌Gogoro再下一城,宣布與德國Bosch集團旗下的新創品牌Coup合作,於德國柏林展開Gogoro電動機車Smartscooter™的租賃服務,正式進軍歐洲。

Gogoro 執行長暨共同創辦人陸學森表示:「Gogoro 與德國BOSCH 集團有一個共同的願景:希望透過COUP 雙輪共享服務提供城市一種更進化的移動方式。」

他指出,選擇以柏林作為歐洲首站的原因,不僅是因為柏林是全球最進步、創新的代表性城市之一,也是因為柏林對機車的依賴度高。透過Coup服務平台,柏林消費者只要年滿21歲、或擁有機車駕照,下載APP後就能騎乘Gogoro電動機車。租用Gogoro電動機車的價格是每30分鐘3歐元,全日租賃則為20歐元。Gogoro初步規劃設置200輛智慧雙輪機車於柏林,並將其最高速度設定為時速45公里,以符合市區騎乘的安全性。

銷售破萬,推回娘家活動

除了成功進軍海外市場之外,Gogoro也宣布台灣市場銷售創下佳績。今年七月,Gogoro智慧雙輪機車在台灣共售出1,300輛,累計車主突破10,000人。為感謝破萬車主的支持,Gogoro從8月4日至9月30日止舉辦車主回娘家活動,只要在Gogoro網路或實體商店消費周邊產品,一律享九折優惠。

陸學森感謝車主與經營團隊的支持,表示:「隨著台灣市場成功達到車主破萬的里程碑,我們也順利的走向國際進軍歐洲,這一切都要歸功於所有車主、員工與夥伴的支持,Gogoro 才能有今天的成績。」

在過去一年間,Gogoro所有車主的總騎乘公里數已接近2,000萬公里,省下85萬公升的汽油、減排1,634公噸的二氧化碳排放。目前Gogoro在全台共有225座GoStation電池交換站,分別位於桃園以北與台中、彰化,每天服務超過7,000顆電池進行交換。

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

工信部:109款純電動車型進入第286批目錄 3款勒令停產

近日,工信部發佈《道路機動車輛生產企業及產品(第286批)》公告。據統計發現,約有109款純電動車型進入此批汽車生產企業目錄,其中專用車和運輸車佔據比例較高。第286批目錄對不符合管理規定的車企產品進行了公示,其中2家企業的3款純電動車型被勒令自公告發佈之日起暫停生產銷售。  
  其中,福建新龍馬汽車股份有限公司涉及的2款車型是FJ6410BEVA1純電動多用途乘用車和FJ6411BEVA1純電動多用途乘用車。資料顯示,FJ6410BEVA1純電動多用途乘用車的動力電池由河南鋰想動力科技有限公司生產,該公司並不在動力電池57家白名單之中。而FJ6411BEVA1純電動多用途乘用車的動力電池由廈門華鋰能源有限公司提供,廈門華鋰也不在動力電池57家白名單之中。   鄭州日產汽車有限公司生產的ZN6440V1Y純電動乘用車也被勒令停止生產銷售。資料顯示,該車型的動力電池由河南環宇賽爾新能源科技有限公司提供,而環宇賽爾在57家電池白名單之中。對於該款車型為何被停止生產銷售,目前不得而知。   文章來源:蓋世汽車

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

特斯拉新款電動汽車比Model 3便宜1萬美金

今年4月1日,特斯拉正式發佈旗下第四款純電動汽車——Model 3,此舉引爆了全球的電動汽車愛好者。不過,特斯拉似乎並沒打算將Model 3設定為自己的“國民車”,據悉,其未來的生產規劃中還有一款更“國民化”的車型。  
  有知情人士透露,特斯拉的“國民車”是一款A級車,售價將比Model 3還低1萬美元,僅2.5萬美元,約合人民幣16.65萬元。不過雖然價格有所下降,但因電池技術不斷提高,成本不斷下降,該款車型的續航里程將仍在300km以上。   該人士還介紹,此款“國民車”應該在2018年左右向全球公佈,2020年交付。屆時,特斯拉將在中國完成國產化,所以中國消費者未來購買該款車型可享受與美國相同的售價。   文章來源:EV世紀

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

【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

一、從零開始搭建自己的靜態博客 — 基礎篇

目錄

前幾天心血來潮,想要在GitHub Pages上搭建一個靜態博客;之前,我也曾基於Django開發過自己的博客,並買了雲主機部署,但是訪問量感人,慢慢自己也不打理了,就把雲主機退訂了(去吃噸好的~~~);

雖然搭建靜態博客很簡單,但是也想記錄一下,如果恰好能對你有所幫助或啟發,那我也覺的很開心了。

搭建靜態博客的工具多種多樣,即有流行的,也有GitHub Pages官方推薦的;其實,選用哪種工具不重要,關鍵是一步步的理解它,遇到問題、解決問題的思路和過程;

因為我本人對Python比較熟悉,所以我選用基於Python開發的,它基本滿足我的需求:

  • 支持markdown的格式;
  • 提供自動化構建;
  • 足夠的主題庫和插件庫,並且支持定製化;

本文主要涉及pelican的基本使用方法,最終在本地搭建一個簡陋的博客網站;

1. 準備環境

選定工作目錄,並使用創建一個虛擬環境:

λ mkdir pelican-blog
λ cd pelican-blog

# 創建基於 Python 3 的虛擬環境 
λ pipenv install --three

# 查看虛擬環境中的 Python 版本
λ pipenv run python --version
Python 3.7.3

在虛擬環境中安裝必要的包:

λ pipenv install Markdown pelican

# 查看包之間的依賴關係
λ pipenv graph
Markdown==3.1.1
  - setuptools [required: >=36, installed: 41.6.0]
pelican==4.2.0
  - blinker [required: Any, installed: 1.4]
  - docutils [required: Any, installed: 0.15.2]
  - feedgenerator [required: >=1.9, installed: 1.9]
    - pytz [required: >=0a, installed: 2019.3]
    - six [required: Any, installed: 1.13.0]
  - jinja2 [required: >=2.7, installed: 2.10.3]
    - MarkupSafe [required: >=0.23, installed: 1.1.1]
  - pygments [required: Any, installed: 2.4.2]
  - python-dateutil [required: Any, installed: 2.8.1]
    - six [required: >=1.5, installed: 1.13.0]
  - pytz [required: >=0a, installed: 2019.3]
  - six [required: >=1.4, installed: 1.13.0]
  - unidecode [required: Any, installed: 1.1.1]

2. 新建項目

pelican提供了一個命令行工具:pelican-quickstart,能夠讓我們快速地新建一個網站項目;

它在執行的過程中,會交互式的詢問一些配置項,如果你現在還不能確定的話,那就大膽的使用默認值吧,後面還可以在配置文件中修改;

命令執行完成后,它會在我們的項目中新建如下的目錄和文件:

.
├── content         # 目錄,存放原始博文和相關靜態文件
├── output          # 目錄,存放構建后的網站源碼
├── Makefile        
├── pelicanconf.py  # 構建相關的配置文件
├── publishconf.py  # 發布相關的配置文件
└── tasks.py

其中,content/目錄存放所有的markdown格式的文本,我們還可以再新建一個content/images/的子目錄,用於存放所有的圖片;

注意:

在自動構建的過程中,content/images/中的文件會被無損地拷貝到output/images/中,通過修改pelicanconf.py文件中STATIC_PATHS的配置項(默認值為['images'])可以改變這種行為;

3. 第一篇博文

現在我們在content/目錄下添加第一篇markdown格式的文章,就以本文為例;

pelican可以很“聰明”地從文章的元數據中提取需要的信息,所以我們以特定的格式編寫文章的開頭:

Title: 一、從零開始搭建自己的靜態博客 -- 基礎篇
Date: 2019-11-21 14:37
Modified: 2019-11-22 11:09
Category: 工具
Tags: pelican
Author: luizyao
Slug: pelican-blog-chapter-1
Summary: 本文簡要的介紹 pelican 的基本用法
Status: published

<開始正文>

注意:

  • 更多元數據以參考:;

  • 如果你使用VSCode作為你的日常開發工具,那麼我建議你使用插件為不同類型的文件自動生成頭信息模版;

4. 修改配置文件

在正式開始構建之前,我們需要完善一下配置文件pelicanconf.py

# pelicanconf.py

# 修改時區
TIMEZONE = 'Asia/Shanghai'

# 添加一個 GitHub 的“絲帶”鏈接
GITHUB_URL = 'https://github.com/luizyao'

# 修改社交賬號的展示
SOCIAL = (
    ('GitHub', 'https://github.com/luizyao'),
)

# 修改默認的時間格式('%a %d %B %Y')
DEFAULT_DATE_FORMAT = "%Y-%m-%d %H:%M"

# 為元數據定義默認值
DEFAULT_METADATA = {
    # 默認發布的文章都是草稿,除非在文章元數據中明確指定:Status: published
    'status': 'draft',
}

5. 本地構建和訪問

我們通過以下命令構建網站並自動適配文件的修改,通過訪問:

λ pipenv run pelican --autoreload --listen content/

注意:

  • 不要忘記把文章元數據中的Status: draft改成Status: published,不然我們是看不到這篇文章的;

  • pelican默認使用notmyidea這個主題來構建網站;你可以通過pelican-themes命令查看已安裝的主題:

    λ pipenv run pelican-themes --list
    notmyidea
    simple

    然後通過在pelicanconf.py中設定THEME = 'simple'或者構建時傳入-t 'simple'選項來使用主題simple,實際上和純文本差不多了;

6. markdown解析異常

  • 這是一個列表:

    if 1:
        print('這是一段python代碼')

這個時候,如果你訪問我們的網站,你會發現上面的markdown代碼被展示成下面的形式,根本就不是我們想要的縮進代碼塊的效果:

為什麼會這樣呢?我們又該如何解決這個問題?

6.1. Markdown包的實現機制

pelican使用包作為markdown文本的解釋器,這個包嚴格實現了語法,並提供一些擴展;

John Grubermarkdown語法的發明者,他在2004發布了第一個版本的markdown語法,這一版本的語法有着明顯的特點:

  • 不支持三個反引號('```')包裹代碼的寫法;
  • 不支持表格;
  • 定義了嚴格的嵌套縮進的格式,必須是4個空格;

雖然自從發布了第一版之後,就再也沒有更新過,但是現在流行的各種markdown語法都是基於它的擴展和補充,例如:、等;

注意:

雖然Markdown包嚴格實現了John Gruber’s Markdown語法,但是具體的實現還是有一些差別的,更多細節可以參考:

6.2. pelican默認使用的Markdown擴展

上一節中我們提到,Markdown包同樣提供一些擴展用於解析更多類型的語法,這些擴展又分為官方擴展和第三方擴展;

通過查閱pelican的源碼(或官方文檔),可以看到其默認使用了以下擴展:

# pelican/settings.py

'MARKDOWN': {
    'extension_configs': {
        'markdown.extensions.codehilite': {'css_class': 'highlight'},
        'markdown.extensions.extra': {},
        'markdown.extensions.meta': {},
    },
    'output_format': 'html5',
},

首先,我們看一下擴展:

它主要實現了大多數PHP Markdown的語法,是其它6個擴展的合集:

擴展 文檔 描述
Abbreviations
Attribute Lists
Definition Lists
Fenced Code Blocks 擴展了代碼塊的寫法
Footnotes
Tables 支持表格

我們重點看一下Fenced Code Blocks,因為它支持了我常用的三個反引號包裹代碼塊的寫法:

GitHub‘s backtick (“`) syntax is also supported:

# more python code

然後,我們再看一下擴展:

它基於包為我們提供了代碼的高亮显示,我們主要看一下它的一些可配置選項:

  • linenums:如果置為True,將會為代碼塊每行標上行號;
  • css_class:為<div>標籤加上class屬性,默認是codehilite;在這裏,pelican使用的是highlight;

最後,我們看一下:

它主要是pelican內部使用,還記得我們每個markdown文本的開頭都要有特定的格式嗎?就是通過這個擴展讀取的;感興趣的同學可以自己去看一下,這裏我們就不多說了;

6.3. 向第三方擴展尋求幫助

看到現在,我們也沒有找到想要的解決方案:對列表裡縮進嵌套反引號包裹的代碼塊,進行正確的渲染;

還好我們還有眾多的第三方擴展供我們使用:

我們找到一個的擴展貌似可以代替markdown.extensions.extra,來一起看一下吧:

它和markdown.extensions.extra大部分是一樣的,只是有以下不同:

  • 新包含了擴展:優化粗體和斜體的展示(不關心);
  • 新包含了擴展:增加了對原始HTML代碼的處理(不關心);
  • 使用擴展代替Fenced Code Blocks:加強版的markdown語法解析(看來正式我們想要的);

其實,看到SuperFences文檔的第一句話,我就知道妥了,嘻嘻;

Allowing the nesting of fences under blockquotes, lists, or other block elements (see Limitations for more info).

文檔的內容很豐富,我們就不再這裏一一解釋了,有興趣的同學可以自己去看一看,說不定有什麼意外的收穫呢!!!

6.4. 解決問題

現在,我們來實際解決這個問題:

  1. 安裝必要的包:

    λ pipenv install pymdown-extensions
  2. 修改pelicanconf.py文件中MARKDOWN的默認配置:

    # 使用第三方擴展來增強對 markdown 語言的解析,但是首先要安裝 pymdown-extensions 模塊
    MARKDOWN = {
        'extension_configs': {
            'markdown.extensions.codehilite': {'css_class': 'highlight'},
            'pymdownx.extra': {},
            'markdown.extensions.meta': {},
        },
        'output_format': 'html5',
    }

7. One more thing

我在瀏覽SuperFences文檔時,發現一個很有意思的章節:;

它推薦了代替markdown.extensions.codehilite,那我們就來看看這到底是個什麼鬼?

在它的文檔中有一句話大概能說明兩者的關係:

The Highlight extension is inspired by CodeHilite, but differs in features. PyMdown Extensions chooses not to implement special language headers for standard Markdown code blocks like CodeHilite does; PyMdown Extensions takes the position that language headers are better suited in fenced code blocks.

更多實現上的細節,我們不再去深究,主要看看我們可以用來干什麼?

比如,為代碼塊每行加上行號:

咦?markdown.extensions.codehilite也可以啊,它不是也有一個linenums的選項嗎?置成True不就行了;

說的對,不過丑。

一般情況下,為代碼塊添加行號有兩種樣式:

  • table:默認的樣式,創建一個表,第一列是行號;
  • inline:在每行代碼的開頭,但是複製代碼會把行號一起複制,不方便;

不過,pymdownx.highlight提供了第三種樣式:pymdownx-inline,它和inline很像,只是複製時不會加上行號,因為實際上把行號元素渲染成下面這樣:

<span class="lineno" data-linenos="1 "></span>

然後,我們通過以下的CSS樣式去“激活”它:

[data-linenos]:before {
  content: attr(data-linenos);
}

下面,我們來將它具體的應用到我們的項目中吧:

首先,修改pelicanconf.py文件中MARKDOWN的默認配置:

# 使用第三方擴展來增強對 markdown 語言的解析,但是首先要安裝 pymdown-extensions 模塊
MARKDOWN = {
    'extension_configs': {
        'pymdownx.highlight': {
            'css_class': 'highlight',
            'linenums': True,
            'linenums_style': 'pymdownx-inline',
        },
        'pymdownx.extra': {},
        'markdown.extensions.meta': {},
    },
    'output_format': 'html5',
}

然後,在output/theme/css/main.css文件的末尾加上下面這段代碼:

[data-linenos]:before {
  content: attr(data-linenos);
}

最後重啟下服務,就能看到效果了:

注意:

這裡有個問題,如果我們重新執行構建命令,output/theme/css/main.css文件又會被覆蓋成原先的內容,我們這個效果就看不到了;

不過這並不是我們最終的方案,所以我們也不在這裏繼續深究了。

GitHub:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

100天搞定機器學習|Day56 隨機森林工作原理及調參實戰(信用卡欺詐預測)

本文是對的補充

前文對隨機森林的概念、工作原理、使用方法做了簡單介紹,並提供了分類和回歸的實例。
本期我們重點講一下:
1、集成學習、Bagging和隨機森林概念及相互關係
2、隨機森林參數解釋及設置建議
3、隨機森林模型調參實戰
4、隨機森林模型優缺點總結

集成學習、Bagging和隨機森林

集成學習

集成學習並不是一個單獨的機器學習算法,它通過將多個基學習器(弱學習器)進行結合,最終獲得一個強學習器。這裏的弱學習器應該具有一定的準確性,並且要有多樣性(學習器之間具有差異),比較常用的基學習器有決策樹和神經網絡。

集成學習的核心就是如何產生並結合好而不同的基學習器,這裡有兩種方式是,一種是Bagging,基學習器之間沒有強依賴關係,可同時生成的并行化方法。一種是Boosting,基學習器之間有強依賴關係,必須串行生成。
集成學習另一個關鍵問題是結合策略,主要有平均法、投票法和學習法,這裏不再展開。

Bagging

Bagging是Bootstrap AGGregaING的縮寫,Bootstrap即隨機採樣,比如給定含有$m$個樣本的數據集$D$,每次隨機的從中選擇一個樣本,放入新的數據集,然後將其放回初始數據集$D$,放回後有可能繼續被採集到,重複這個動作$m$次,我們就得到新的數據集$D’$。

用這種方式,我們可以採樣出TGE含m個訓練樣本的採樣集,然後基於每個採樣集訓練基學習器,再將基學習器進行結合,這便是Bagging的基本流程。

隨機森林
隨機森林是非常具有代表性的Bagging集成算法,它在Bagging基礎上進行了強化。
它的所有基學習器都是CART決策樹,傳統決策樹在選擇劃分屬性時是在當前結點的屬性集合(假定有d個屬性)中選擇最優屬性。但是隨機森林的決策樹,現在每個結點的屬性集合隨機選擇部分k個屬性的子集,然後在子集中選擇一個最優的特徵來做決策樹的左右子樹劃分,一般建議$k=log_2d$.分類決策樹組成的森林就叫做隨機森林分類器,回歸決策樹所集成的森林就叫做隨機森林回歸器。

RF的算法:

輸入為樣本集$D={(x_,y_1),(x_2,y_2), …(x_m,y_m)}$,弱分類器迭代次數T。

輸出為最終的強分類器$f(x)$

1)對於t=1,2…,T:
a)對訓練集進行第t次隨機採樣,共採集m次,得到包含m個樣本的採樣集Dt
b)用採樣集$D_t$訓練第t個決策樹模型$G_t(x)$,在訓練決策樹模型的節點的時候, 在節點上所有的樣本特徵中選擇一部分樣本特徵, 在這些隨機選擇的部分樣本特徵中選擇一個最優的特徵來做決策樹的左右子樹劃分

2)如果是分類算法預測,則T個弱學習器投出最多票數的類別或者類別之一為最終類別。如果是回歸算法,T個弱學習器得到的回歸結果進行算術平均得到的值為最終的模型輸出。

隨機森林參數解釋及設置建議

在scikit-learn中,RandomForest的分類類是RandomForestClassifier,回歸類是RandomForestRegressor,需要調參的參數包括兩部分,第一部分是Bagging框架的參數,第二部分是CART決策樹的參數。這裏我們看一下scikit-learn中隨機森林的主要參數

隨機森林模型調參實戰

這是一道kaggle上的題目,通過信用卡交易記錄數據對欺詐行為進行預測,信用卡欺詐檢測文件記錄了2013年9月歐洲信用卡持有者所發生的交易。在284807條交易記錄中共包含492條欺詐記錄。
數據集下載地址:請在公眾號後台回復[56]
需要說明的是,本文重點是RF模型調參,所以不涉及數據預處理、特徵工程和模型融合的內容,這些我會在本欄目未來的章節中再做介紹。
所以最終結果可能會不理想,這裏我們只關注通過調參給模型帶來的性能提升和加深對重要參數的理解即可。
1、導入用到的包

import numpy as np
import pandas as pd
from sklearn.model_selection import GridSearchCV,train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import roc_auc_score

2、導入數據

df = pd.read_csv("D:\WKS\PyProject\Credit_Card\creditcard.csv")
data=df.iloc[:,1:31]

284807條交易記錄中只有492條欺詐記錄,樣本嚴重不平衡,這裏我們需要使用下採樣策略(減少多數類使其數量與少數類相同)

X = data.loc[:, data.columns != 'Class']
y = data.loc[:, data.columns == 'Class']

number_records_fraud = len(data[data.Class == 1]) # class=1的樣本函數
fraud_indices = np.array(data[data.Class == 1].index) # 樣本等於1的索引值

normal_indices = data[data.Class == 0].index # 樣本等於0的索引值

random_normal_indices = np.random.choice(normal_indices,number_records_fraud,replace = False)
random_normal_indices = np.array(random_normal_indices)

under_sample_indices = np.concatenate([fraud_indices,random_normal_indices]) # Appending the 2 indices

under_sample_data = data.iloc[under_sample_indices,:] # Under sample dataset

X_undersample = under_sample_data.loc[:,under_sample_data.columns != 'Class']
y_undersample = under_sample_data.loc[:,under_sample_data.columns == 'Class']

X_train, X_test, y_train, y_test = train_test_split(X_undersample,y_undersample,test_size = 0.3, random_state = 0)

先用默認參數訓練RF

rf0 = RandomForestClassifier(oob_score=True, random_state=666)
rf0.fit(X_train,y_train)
print(rf0.oob_score_)
y_predprob = rf0.predict_proba(X_test)[:,1]
print("AUC Score (Train): %f" % roc_auc_score(y_test, y_predprob))

0.9244186046511628
AUC Score (Train): 0.967082
除oob_score將默認的False改為True, 我們重點優化n_estimators、max_depth、min_samples_leaf 這三個參數。為簡單起見,模型評價指標,我們選擇AUC值。
模型調優我們採用網格搜索調優參數(grid search),通過構建參數候選集合,然後網格搜索會窮舉各種參數組合,根據設定評定的評分機制找到最好的那一組設置。
先優化n_estimators

param_test1 = {'n_estimators':range(10,101,10)}
gsearch1 = GridSearchCV(estimator = RandomForestClassifier(oob_score=True, random_state=666,n_jobs=2), 
                       param_grid = param_test1, scoring='roc_auc',cv=5)
gsearch1.fit(X_train,y_train)
gsearch1.cv_results_, gsearch1.best_params_, gsearch1.best_score_

{‘n_estimators’: 50},
0.9799524239675649)
在優化后的n_estimators基礎上,優化max_features

param_test2 = {'max_depth':range(2,12,2)}
gsearch2 = GridSearchCV(estimator = RandomForestClassifier(n_estimators= 50,oob_score=True, random_state=666,n_jobs=2),
   param_grid = param_test2, scoring='roc_auc',cv=5)
gsearch2.fit(X_train,y_train)
gsearch2.cv_results_, gsearch2.best_params_, gsearch2.best_score_

{‘max_depth’: 6},
0.9809897227343921)
在上述兩個參數優化結果的基礎上優化max_depth

param_test2 = {'min_samples_split':range(2,8,1)}
gsearch2 = GridSearchCV(estimator = RandomForestClassifier(n_estimators= 50,max_depth=6,
                                  oob_score=True, random_state=666,n_jobs=2),
   param_grid = param_test2, scoring='roc_auc',cv=5)
gsearch2.fit(X_train,y_train)
gsearch2.cv_results_, gsearch2.best_params_, gsearch2.best_score_

{‘min_samples_split’: 5},
0.9819618127837587)

最後我們綜合再次嘗試

rf1 = RandomForestClassifier(n_estimators= 50,max_depth=6,min_samples_split=5,oob_score=True, random_state=666,n_jobs=2)
rf1.fit(X_train,y_train)
print(rf1.oob_score_)
y_predprob1 = rf1.predict_proba(X_test)[:,1]
print("AUC Score (Train): %f" % roc_auc_score(y_test, y_predprob1))

0.9331395348837209
AUC Score (Train): 0.977811
最終結果比調參前有所提升

隨機森林優缺點總結

RF優點
1.不容易出現過擬合,因為選擇訓練樣本的時候就不是全部樣本。
2.可以既可以處理屬性為離散值的量,比如ID3算法來構造樹,也可以處理屬性為連續值的量,比如C4.5算法來構造樹。
3.對於高維數據集的處理能力令人興奮,它可以處理成千上萬的輸入變量,並確定最重要的變量,因此被認為是一個不錯的降維方法。此外,該模型能夠輸出變量的重要性程度,這是一個非常便利的功能。
4.分類不平衡的情況時,隨機森林能夠提供平衡數據集誤差的有效方法
RF缺點
1.隨機森林在解決回歸問題時並沒有像它在分類中表現的那麼好,這是因為它並不能給出一個連續型的輸出。當進行回歸時,隨機森林不能夠作出超越訓練集數據範圍的預測,這可能導致在對某些還有特定噪聲的數據進行建模時出現過度擬合。
2.對於許多統計建模者來說,隨機森林給人的感覺像是一個黑盒子——你幾乎無法控制模型內部的運行,只能在不同的參數和隨機種子之間進行嘗試。

參考:

https://www.jianshu.com/p/708dff71df3a
https://zhuanlan.zhihu.com/p/30461746
https://www.cnblogs.com/pinard/p/6156009.html

《百面機器學習》中有一道關於隨機森林的面試題,大家可以思考一下:
可否將隨機森林中的基分類器由決策樹替換為線性分類器或K-近鄰呢?

解答:隨機森林屬於Bagging類的集成學習,Bagging的主要好處是集成后的分類器的方差比基分類器方差小。Bagging採用的分類器最好是本身對樣本分佈比較敏感(即不穩定的分類器),這樣Bagging才有價值。線性分類器或K-近鄰都是比較穩定,本身方差就很小,所以以他們作為基分類器使用Bagging並不能獲得更好地表現,甚至可能因為Bagging的採樣導致訓練中更難收斂,從而增大集成分類器的偏差。

本文由博客一文多發平台 發布!

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

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

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!